我有这个功能from another SO question,
f :: Ord a => [a] -> [(a, Int)]
f xs = zipWith (\x ys -> (x, length $ filter (< x) ys)) xs (inits xs)
我正在尝试用无点样式编写它,
f = flip (zipWith (\x -> (,) x . length . filter (< x))) =<< inits
是否有可能摆脱那个x?
答案 0 :(得分:8)
可能,但绝对不值得痛苦。要直接回答您的问题,FreeNode上的LambdaBot报告:
f = flip (zipWith (liftM2 (.) (,) ((length .) . filter . flip (<)))) =<< inits
此时,该功能已经失去了它的清晰度,并且已经变得无法维护。在这里,您可以更好地介绍真实姓名。请记住,仅仅因为我们可以将事情指向自由并不意味着我们应该这样做。
答案 1 :(得分:6)
作为一般规则:如果变量在表达式中出现多次,使它成为无点可能不是一个好主意。但是,如果你确定了,那么最不可读的方法就是使用Arrow
组合器,因为这样可以清楚地说明数据流的位置是什么&#34; split&#34;。对于xs
我写
uncurry (zipWith (...)) . (id &&& inits)
对于x
,相同的方法产生
zipWith ( curry $ uncurry(,) . (fst &&& length . uncurry filter . first(>)) )
这比你使用的(->)
- monad解决方案和lambdabot建议的更长,但看起来更有条理。
答案 2 :(得分:5)
pointfree样式的重点不仅仅是省略值的名称,而是更喜欢的函数名称。使用非常小的定义时,这很容易做到。当然,如果你内联所有内容并且不使用好名字,任何代码都将变得不可读。
因此,让我们从您的原始函数开始,并将其拆分为几个较小的定义。
f xs = zipWith combine xs (inits xs)
combine x xs = (x, countWhere (< x) xs)
countWhere f xs = length (filter f xs)
现在我们可以轻松地以可读的方式使这些定义无点。
f = zipWith combine <*> inits
where combine = compose (,) countLessThan
compose = liftA2 (.)
countLessThan = countWhere . flip (<)
countWhere = length .: filter
(.:) = (.) . (.)
明智地使用名称 并且更喜欢使用组合而不是应用程序允许我们将代码分解为易于理解的小定义。对于数据功能强大,命名参数相当于goto
,但最适合用于构建易于理解和正确使用的可重用高级结构。这些组合组合器(例如(.)
和<*>
)用于数据流map
,filter
和fold
来控制流量。
答案 3 :(得分:4)
我刺伤了它:
f :: Ord a => [a] -> [(a, Int)]
f = zip <*> ((zipWith $ (length .) . filter . (>)) <*> inits)
在此,我将(<)
替换为(>)
,使(length .) . filter . (>)
作为函数使用正确的参数:a->[a]->Int
。将其传递给zipWith
,我们得到[a]->[[a]]->[Int]
。
假设我们输入[a]
,我们可以将f ([[a]]->[Int])
视为Applicative ((->) [a])
,inits :: f [[a]]
可与<*> :: f ([[a]]->[Int])->f [[a]]->f [Int]
结合使用[a]->[Int]
。这给了我们[a]
,现在需要并行使用[Int]
和zip
。 [a]->[Int]->[(a,Int)]
已经是正确的类型:<*>
要与{{1}}一起使用。
答案 4 :(得分:3)
不是说我推荐这个,但是Pointfree之王是Control.Arrow
import Control.Arrow
-- A special version of zipWith' more amenable to pointfree style
zipWith' :: ((a, b) -> c) -> ([a], [b]) -> [c]
zipWith' = uncurry . zipWith . curry
f :: Ord a => [a] -> [(a, Int)]
f = zipWith' (fst &&& (length <<< uncurry filter <<< first (>))) <<< id &&& inits
让我在这里重新说明 - 我真的不推荐这个,除非你的意图是以某种方式概括你的程序运行的箭头类型(例如,也许是箭头化的FRP)。
答案 5 :(得分:2)
着名的
(f .: g) x y = f (g x y)
它是半可读的
zipWith (curry (fst &&& uncurry (length .: (filter . flip (<))) )) <*> inits
-- \(x,ys) -> (x , length ( (filter . flip (<)) x ys) )
使用Control.Applicative
(f <*> g $ x = f x (g x)
, S 组合子)和Control.Arrow
(与其他人一样,但有点不同)。