如何将两个参数的Haskell函数重写为无点样式

Jon*_*caC 25 haskell pointfree

我在Haskell中有以下功能

agreeLen :: (Eq a) => [a] -> [a] -> Int
agreeLen x y = length $ takeWhile (\(a,b) -> a == b)  (zip x y)
Run Code Online (Sandbox Code Playgroud)

我正在努力学习如何编写'惯用'Haskell,它似乎更喜欢使用.$不是括号,并且在可能的情况下更喜欢无点代码.我似乎无法摆脱提及xy明确.有任何想法吗?

我认为我对点两个参数的任何函数都有同样的问题.

顺便说一下,这只是为了编写好的代码; 不是一些"使用任何必要的东西来使其无意义"的家庭作业.

谢谢.


(添加评论)感谢您的回答.你已经说服我这个功能不会从pointfree中受益.而且你也给了我一些很好的例子来练习转换表达式.这对我来说仍然很困难,而且它们似乎与Haskell一样重要,因为它指向C.

Dan*_*her 46

并且在可能的情况下更喜欢无点代码.

不是"在可能的地方",而是"它提高可读性(或具有其他明显优势)".

要点免费

agreeLen x y = length $ takeWhile (\(a,b) -> a == b)  (zip x y)
Run Code Online (Sandbox Code Playgroud)

第一步是($)向右移动,用一个替换你拥有的那个(.):

agreeLen x y = length . takeWhile (\(a,b) -> a == b) $ zip x y
Run Code Online (Sandbox Code Playgroud)

现在,您可以更进一步地移动它:

agreeLen x y = length . takeWhile (uncurry (==)) . zip x $ y
Run Code Online (Sandbox Code Playgroud)

在那里你可以立即砍掉一个论点,

agreeLen x = length . takeWhile (uncurry (==)) . zip x
Run Code Online (Sandbox Code Playgroud)

然后你可以将其重写为合成运算符的前缀应用程序,

agreeLen x = (.) (length . takeWhile (uncurry (==))) (zip x)
Run Code Online (Sandbox Code Playgroud)

你可以写

f(gx)

f . g $ x
Run Code Online (Sandbox Code Playgroud)

一般来说,这里

f = (.) (length . takeWhile (uncurry (==)))
Run Code Online (Sandbox Code Playgroud)

并且g = zip,给

agreeLen x = ((.) (length . takeWhile (uncurry (==)))) . zip $ x
Run Code Online (Sandbox Code Playgroud)

从中可以x轻松删除参数.然后你可以将前缀应用程序(.)转换为一个部分并获取

agreeLen = ((length . takeWhile (uncurry (==))) .) . zip
Run Code Online (Sandbox Code Playgroud)

但是,这比原版的可读性差,所以除了练习将表达式转换为无点样式之外,我不建议这样做.

  • 真正引人注目的是,一个名叫Schönfinkel的人在1920年发明了这种技术. (12认同)

Gab*_*lez 12

你也可以使用:

agreeLen :: (Eq a) => [a] -> [a] -> Int
agreeLen x y = length $ takeWhile id $ zipWith (==) x y
Run Code Online (Sandbox Code Playgroud)

惯用的Haskell是更容易阅读的东西,不一定是最无点的东西.

  • @Jonathan嗯,即使社区对样式有不同的看法,但我可以告诉你,我自己的约定是每当你有一个参数的函数链时使用`.`,如果你使用两个或多个参数,你只需要它们明确.对于`$`,你主要用它来删除`do`块末尾的悬空括号,例如`foo $ do ...`,或者整理多重嵌套的括号. (4认同)