25

我在 Haskell 中有以下功能

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

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

我认为我会遇到同样的问题,即对两个参数的任何函数进行点释放。

BTW,这只是为了写好代码;不是一些“尽一切可能使它变得毫无意义”的家庭作业。

谢谢。


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

4

4 回答 4

48

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

不是“在可能的情况下”,而是“在提高可读性(或具有其他明显优势)的地方”。

免积分

agreeLen x y = length $ takeWhile (\(a,b) -> a == b)  (zip x y)

第一步是($)向右移动,并将您拥有的替换为(.)

agreeLen x y = length . takeWhile (\(a,b) -> a == b) $ zip x y

现在,您可以将其进一步向右移动:

agreeLen x y = length . takeWhile (uncurry (==)) . zip x $ y

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

agreeLen x = length . takeWhile (uncurry (==)) . zip x

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

agreeLen x = (.) (length . takeWhile (uncurry (==))) (zip x)

你可以写

f (gx)

作为

f . g $ x

一般来说,这里有

f = (.) (length . takeWhile (uncurry (==)))

g = zip,给

agreeLen x = ((.) (length . takeWhile (uncurry (==)))) . zip $ x

x很容易从中删除论点。然后你可以将前缀应用(.)转换为一个部分并得到

agreeLen = ((length . takeWhile (uncurry (==))) .) . zip

但是,这比原来的可读性差,所以我不建议这样做,除非练习将表达式转换为无点样式。

于 2012-11-17T00:51:10.947 回答
12

您还可以使用:

agreeLen :: (Eq a) => [a] -> [a] -> Int
agreeLen x y = length $ takeWhile id $ zipWith (==) x y

惯用的 Haskell 是更容易阅读的东西,不一定是最无意义的东西。

于 2012-11-17T00:43:25.560 回答
2

正如丹尼尔的出色回答所指出的那样,您的问题是撰写fg何时f作为一个论点和g两个论点。这可以f ??? g用正确的运算符编写(并且类型签名为(c -> d) -> (a -> b -> c) -> a -> b -> d。这对应于有时定义为的(.).(.)运算符(参见那里.:) 。在这种情况下,您的表达式变为

length . takeWhile (uncurry (==)) .: zip

如果你习惯了.:操作符,那么这个免费版本是完全可读的。我也可以改为使用 (<$$$>) = fmap fmap fmap并获取

length . takeWhile (uncurry (==)) <$$$> zip
于 2018-06-29T08:25:36.527 回答
1

另一个简洁、无点的解决方案:

agreeLen = ((length . takeWhile id) .) . zipWith (==)

等效地:

agreeLen = (.) (length . takeWhile id) . zipWith (==)
于 2018-06-29T06:39:05.383 回答