【发布时间】:2015-12-31 14:56:10
【问题描述】:
我有这段代码:
palindrome :: String -> Bool
palindrome x = x == reverse x
有没有什么方法可以用无点风格重写它?
【问题讨论】:
我有这段代码:
palindrome :: String -> Bool
palindrome x = x == reverse x
有没有什么方法可以用无点风格重写它?
【问题讨论】:
是的,因为任何函数都可以用无点样式编写。在这里,(->) r(又名 Reader)的 Applicative 实例为您执行此操作,因为
(f <*> g) x = f x (g x)
您可能会认出这是来自SKI calculus 的 S 组合子(顺便说一下,return 是 K)。
你的回文检查器写成
x == reverse x
中缀形式的读法
(==) x (reverse x)
与上面的<*> 定义相比,这导致了表达式
isPalindrome x = ((==) <*> reverse) x
您可以在其中删除尾随的 x 以获得解决方案
isPalindrome = (==) <*> reverse
这可能比原始表达式可读性差,因此不应使用。无点样式是为了可读性,并且仅在某些情况下有用。
【讨论】:
map 的可读性总是不如列表理解。可读性实际上取决于流行的习语。看惯了applicative style的人可能会把point-free的版本理解为“平等高于同一性和颠倒”之类的,这很容易理解。流行的成语随着时间的推移而变化,所以如果你想让它朝着特定的方向变化,你可以谨慎使用“奇怪”的成语。
<*> 和 join 应该是 a basic repertoire for point-free style 的一部分,它可以自己学习(除了作为 Applicative 实例)。
你可能认为这种方法是作弊:
palindrome :: Eq a => [a] -> Bool
palindrome = palindrome'
where palindrome' xs = xs == reverse xs
当然还有 David 和 freyrs 建议的应用风格:
palindrome'' :: Eq a => [a] -> Bool
palindrome'' = (==) <*> reverse
但是这个表达式作为折叠怎么样?
palindrome''' :: Eq a => [a] -> Bool
palindrome''' = (foldl (\b (x, y) -> b && x == y) True)
. (uncurry zip)
. reverse'
where reverse' xs = (xs, reverse xs)
【讨论】:
reverse' 不是无点的,所以你可以说这和你的第一个一样。要使reverse' 无点,您将再次需要应用版本,使其与您的第二个示例相同! (foldl lambda 也不是无点的,但我有一种感觉,它只是从 Data.List 重新实现了 all,因此可以快速使表达式变得无点。)
(&&) 只是 and: palindrome = and . uncurry (zipWith (==)) . join ((,).reverse)。但当然and . uncurry (zipWith (==)) 只是uncurry (==) 的一个实现,正如the answer by swish 所举例说明的那样。 :)
(->) r 也是一个 Monad,所以你的回文检查器可以用 monadic bind 编写,这可能比上面的 Applicative 解决方案更具可读性
palindrome :: String -> Bool
palindrome = reverse >>= (==)
【讨论】:
ap k g == g >>= (flip k) == k <*> g == \x -> k x (x r)。所以你的代码表示reverse xs == xs。
是的。
palindrome :: String -> Bool
palindrome = ap (==) reverse
【讨论】:
palindrome :: String -> Bool
palindrome = uncurry (==) . (id &&& reverse)
(&&&) 在Control.Arrow 中定义,因此(f &&& g) x = (f x, g x)。
【讨论】: