本人菜鸟...之前没特意研究过函数式编程
最近再做一个三消游戏,写了一点 Ramda ,我发现一个问题
下面这段大概意思是根据 row.length 判断当前这个格子需不需要填充砖块,根据结果返回砖块值或 null
最后 prepend 到 map 里
compose(
prepend,
ifElse(identity<boolean>, brickSample, always(null)),
shouldFill
)(row.length)(row),
问题是我写了很多经常出现(arg)(map)
这样的代码
直觉这样写不对,因为我看别人的代码没有这么写过,也许根据入参获取砖块值是一个流程,prepend 到地图里是另外一个流程
就又改成这样了:
prepend(
compose(
ifElse(identity<boolean>, brickSample, always(null)),
shouldFill
)(row.length),
row
),
但是还是感觉很乱,有更好看的写法吗?
1
witcat OP 突然想起来可以用 copilot ,去充了一个,对学习很有帮助。
贴两个 copilot 写的 😂 ``` ifElse( shouldFill, compose(prepend, brickSample), compose(prepend, always(null)) )(row.length)(row), ``` ``` prepend( ifElse(shouldFill, brickSample, always(null))(row.length), row ), ``` |
2
lmshl 2022-12-05 13:35:10 +08:00 1
|
3
Leviathann 2022-12-05 13:44:25 +08:00
point free 就是要把代码写成文章
如果无法从语义上(也就是函数名)表达代码在做什么那就是没写好,或者说这段代码不适合 point free |
4
wanacry 2022-12-05 13:46:44 +08:00
关于 Ramda 的写法,它提供了一些函数组合的工具函数,例如 compose 、pipe 等,可以用来把多个函数组合在一起,并且从右往左依次执行。
对于上面的代码,可以把两个部分分别写成两个函数,然后用 compose 组合在一起,代码如下: const getBrick = compose( ifElse(identity<boolean>, brickSample, always(null)), shouldFill ) const prependBrick = (map) => (row) => prepend(getBrick(row.length)(row), map) prependBrick(map)(row) 这样就可以很清晰地看出来,前面的部分是根据 row.length 获取砖块值,后面的部分是 prepend 到地图里。 当然,这个写法并不是必须的,你可以按照自己的喜好写,只要保证代码可读性即可。 |
6
lmshl 2022-12-05 13:54:46 +08:00
不辣的
```javascript if (!shouldFill(row.length)) return row const arr = brickSample() return prepend(row, arr) ``` |
7
novolunt 2022-12-05 14:22:39 +08:00
// Define named functions for each part of the code.
function shouldFill(length: number) { // Determine whether the current cell should be filled with a brick. return length > 0; } function brickSample(filled: boolean) { // If the cell should be filled, return the value of a brick. // Otherwise, return null. return filled ? BRICK_VALUE : null; } function prependBrick(brick: any, row: any[]) { // Prepend the given brick to the given row. return [brick, ...row]; } // Use the named functions to create a new, composed function. const fillRow = compose( prependBrick, brickSample, shouldFill ); // Use the composed function to fill the cells in a row. const filledRow = fillRow(row.length, row); |
8
cnhongwei 2022-12-05 14:54:07 +08:00
开眼了,感觉 Ramda 真的是函数式。但程序变得好神奇。
看了之后,还是坚定的自己的习惯,保持数据不可变,用好 map flatmap filter reduce 就可以好好的函数式编程了。 |
9
libook 2022-12-05 17:50:07 +08:00
做了 10 年 JS 全栈开发,表示两段看起来都很难读懂。
一般 JS 开发者的思维都是 foo() 或 foo=() 这种形式,即函数名后紧跟参数表的形式,所以看到 ()()() 这种形式就需要转换思维,先在脑内模拟运行前面的部分才能知道返回了什么函数来使用后面的参数表,在代码特别长的时候,单拎出来中间一部分可能无法确定是在执行什么函数,可能降低了代码阅读理解的效率。工业上来讲,个人认为 a().b().c() 这样链式调用的形式可能更明确。 通常,函数调用和逻辑判断流程有明显的语法格式差异,可以帮助阅读者快速切换这两种语境,但当流程控制也被封装成函数的时候,就需要人去了解函数内部的流程特征,个人认为不如直接在相关代码附近喂给阅读者要方便,在各个函数声明的区域跳来跳去也比较打断思路。 或许题主只想做一个使用 JS 实现的完全函数式编程案例,尽管这不是工业上最普遍的风格;这样的话题主就不要纠结好看不好看了,符合教材范式和核心目标就好吧。 |