我知道在某些语言(Haskell?)中,努力是实现无点样式,或者永远不要通过名称显式引用函数参数。这对我来说是一个非常难以掌握的概念,但它可以帮助我理解这种风格的优点(或者甚至是缺点)。谁能解释一下?
答案 0 :(得分:73)
答案 1 :(得分:56)
我认为目的是简洁,并将流水线计算表达为功能组合,而不是通过思考线程参数。简单的例子(在F#中) - 给定:
let sum = List.sum
let sqr = List.map (fun x -> x * x)
用过:
> sum [3;4;5]
12
> sqr [3;4;5]
[9;16;25]
我们可以将“平方和”表示为:
let sumsqr x = sum (sqr x)
并使用如下:
> sumsqr [3;4;5]
50
或者我们可以通过管道x来定义它:
let sumsqr x = x |> sqr |> sum
以这种方式编写,很明显x正在中传递以通过一系列函数“线程化”。直接构图看起来更好:
let sumsqr = sqr >> sum
这是更简洁的,它是一种不同的思考我们正在做的事情的方式;编写函数而不是想象论证流程的过程。我们没有描述sumsqr
的工作原理。我们正在描述是的内容。
: sumsqr sqr sum ;
)单词之间的空格是组合运算符。
PPS:也许其他人可以评论性能差异。在我看来,组合可以通过使编译器更加明显来减少GC压力,因为不需要像流水线那样产生中间值;帮助使所谓的“砍伐森林”问题更容易处理。
答案 2 :(得分:3)
虽然我对无点概念感兴趣并且将其用于某些事情,并且同意之前所说的所有正面观点,但我发现这些事情都是负面的(有些在上面详述):
较短的符号可减少冗余;在一个结构严重的组合(ramda.js样式,或Haskell中的无点,或任何连接语言)中,代码读取比线性扫描一堆const
绑定并使用符号高亮显示更复杂绑定进入其他下游计算。除了树与线性结构之外,描述性符号名称的丢失使得该功能难以直观地掌握。当然,树结构和命名绑定的丢失也有很多好处,例如,函数会感觉更通用 - 不通过选择的符号名称绑定到某个应用程序域 - 并且树结构在语义上甚至存在如果绑定布局,并且可以顺序理解(lisp let / let * style)。
在通过管道或组合一系列功能时,无点是最简单的,因为这也会导致我们人类容易理解的线性结构。但是,通过多个收件人进行某些临时计算是繁琐的。有各种各样的包装到元组,透镜和其他刻薄的机制只是让一些计算可访问,否则只是多次使用一些值绑定。当然,重复的部分可以作为一个单独的函数提取出来,也许它无论如何都是个好主意,但也有一些非短函数的论据,即使它被提取,它的论点也必须以某种方式穿过两个应用程序,然后可能需要记住该函数以不实际重复计算。一个人将使用大量converge
,lens
,memoize
,useWidth
等。
特定于JavaScript:难以随意调试。通过线性流let
绑定,可以轻松地在任何地方添加断点。使用无点样式,即使以某种方式添加断点,也很难读取值流,例如。您无法在开发控制台中查询或悬停某个变量。此外,由于无点在JS中不是原生的,ramda.js或类似的库函数会使堆栈模糊不清,尤其是在专用curry时。
代码脆弱性,特别是在非平凡大小的系统和生产中。如果出现了新的要求,那么上述缺点就会发挥作用(例如,更难以阅读下一个维护者的代码,他们可能会在几周后自己查找,并且更难以跟踪数据流以进行检查)。但最重要的是,即使是看似小而且无辜的新要求,也需要完整不同的代码结构。可能有人认为它是一件好事,因为它可以清晰地表达新事物,但重写大量无点代码是非常耗时的,然后我们就没有了。提到测试。因此,感觉更松散,更少结构化,基于词汇分配的编码可以更快地重新利用。特别是如果编码是探索性的,并且在具有奇怪约定(时间等)的人类数据领域中很少能够100%准确地捕获,并且可能总是存在即将要求的处理更准确或更符合需求的请求。顾客,无论采用哪种方法,都可以加快枢转速度。
答案 3 :(得分:-1)
对于pointpoint变体,连接编程语言,我必须编写:
我对Joy有一点经验。欢乐是一个非常简单且美观的清单概念。将问题转换为Joy函数时,您必须将大脑分成一部分用于堆栈管道工作,一部分用于Joy语法中的解决方案。堆栈始终从背面进行处理。由于合成包含在Joy中,因此合成组合器没有计算时间。