在函数式编程书籍中,反复出现的主题是FP意味着告诉计算机该做什么,而不是怎么做。一些代码示例是给出的,如:
val newNumbers = oldNumbers.map(_ * 2)
我们解释说我们不使用传统的for-loop
来实现这一点,但我们宁愿使用map
。
但是map
内部没有为循环实现。所以我们刚刚将代码转移到其他部分。
那么究竟是什么让FP比命令式风格更好呢?
答案 0 :(得分:1)
让我们继续你的榜样,让所有价值加倍。
function double(arr) { newArray = [] for(index = 0; index < arr.length; i++) { newArr.push(arr[index] * 2) } return newArray }
arr.map(double)
map函数隐藏并抽象实现,而不是一遍又一遍地重新实现它。作为一个地图用户,我不知道函数是用循环,递归还是用完全不同的编程语言实现的。
重点是必须正确地完成工作。
const map = fn => xs => {
let newArray = []
for(let i = 0; i < xs.length; i++) {
newArray.push( fn(xs[i]) )
}
return newArray
}
const double = x =>
x * 2
console.log(
map (double) ([1, 2, 3, 4])
)
const map = fn => xs =>
_map (fn) (xs) ([])
const _map = fn => xs => ys =>
xs.length === 0
? ys
: _map (fn) (xs.slice(1)) (ys.concat( fn(xs[0]) ))
const double = x =>
x * 2
console.log(
map (double) ([1, 2, 3, 4])
)
答案 1 :(得分:1)
但是不是内部实现for循环的map函数
通常不是(至少在函数式语言中)。在一些不那么多范式的函数式语言中,for循环甚至都不存在。
map
的常见实现是:
map(f, []) = []
map(f, x::xs) = f(x) :: map(f, xs)
那么究竟是什么让FP比命令式风格更好?
因为即使map
恰好用for循环实现,使用map
的代码也可以在没有任何可变变量或数据结构的情况下编写。
因此,不是将列表放入可变变量并从循环内重新分配,或者更糟糕的是,使用可从for循环插入元素的可变列表,您可以拥有包含不可变列表的不可变变量无论幕后发生什么,它都能奏效。
答案 2 :(得分:0)
评论太长了,可能确实是一个答案。
我将借用Apocalisp的评论:
for循环在内部由跳转指令(goto)实现。但你不会放弃循环并在任何地方使用gotos只是因为这是循环的实现方式
由于以下原因,我们不再使用GOTO(或longjmp / setjmp):
相反,我们使用for
,while
等等(是的,map
)。我们意味着什么更清楚,而且用这些结构犯错也更难(尽管我仍然认为太容易了)。
如何证明FP得分超过命令式?我可以很好地将命令式实现放在一个编码良好的方法中,并在我需要的任何地方调用它
是的,你可以这样做。你不应该,出于同样的原因,你应该使用通常喜欢经过充分测试和流行的第三方库或内置语言结构自己滚动一切。即使你是一个很棒的开发者,你也无法与热门编译器的编写者(往往是该领域最优秀的人)或数百名贡献者在一个流行的开源库中修复边缘案例错误。
我可以强制实现一个方法,它返回一个新的列表而不修改输入一个,并在我需要的地方调用这个方法
是。再说一次,你可以做到这一点。但请考虑以下因素:
function doubleEvery(arr) {
let i;
let l = arr.length;
let result = [];
for (i=0; i<length; ++i) {
result.push(arr[i] * 2);
}
return result;
}
可能出现什么问题?
function doubleEvery(arr) { // iterating is coupled to doubling
let i; // unitialized variable
let l = arr.length;
// Have to allocate result object, no opportunity for compiler
// to optimize it away or use structural sharing
let result = [];
for (i=0; i<length; ++i) { // possible off-by-one error
// Here if it's a more complex operation than simply doubling
// we can't just pull this part out to test it independently
result.push(arr[i] * 2);
}
return result;
}
另外,原始版本是9行,与arr.map(x => x * 2)
相比。这节省了90%的LoC,在工具,网络和磁盘上更容易。
我认为Apocalisp指出的是你所提出的论点容易受到 reductio ad absurdum 的影响:它可以应用于任何抽象。什么样的男人?你写语法? Real programmers write操作码直接二进制哟。