Haskell中无点函数的先决条件是什么?

hgi*_*sel 2 haskell function pointfree

我一直认为无点函数的先决条件是将函数参数放到定义的末尾.例如

-- This can be made pointfree quite easily:    
let lengths x = map length x    
let lengths' = map length

-- However this cannot:
let lengthz x = length `map` x
-- let lengthz' = length `map` (parse error)
Run Code Online (Sandbox Code Playgroud)

我最初看到这个问题.我们有这个例子:

agreeLen :: (Eq a) => [a] -> [a] -> Int
agreeLen x y = length $ takeWhile id $ zipWith (==) x y
-- This may look like it can easily be made pointfree, however it cannot
-- agreeLen' :: (Eq a) => [a] -> [a] -> Int
-- agreeLen' = length $ takeWhile id $ zipWith (==) (zipWith is applied to too few arguments)
Run Code Online (Sandbox Code Playgroud)

那么为什么我的第一个例子可以成为无点的,但另外两个不可以?

dup*_*ode 7

-- However this cannot:
let lengthz x = length `map` x
-- let lengthz' = length `map` (parse error)
Run Code Online (Sandbox Code Playgroud)

\x -> length `map` x书面点免费简单map length.中缀反引号只是语法糖.(正如chepner所指出的,如果你真的想要它,你可以使用一个部分,即(length `map`).)

agreeLen :: (Eq a) => [a] -> [a] -> Int
agreeLen x y = length $ takeWhile id $ zipWith (==) x y
-- This may look like it can easily be made pointfree, however it cannot
Run Code Online (Sandbox Code Playgroud)

这里的关键词是"容易".如果你足够努力的话,几乎任何事情都可以毫无关键.在这种情况下,省略的y参数很容易,如果我们写agreeLen的方面(.),而不是($):

agreeLen x y = (length . takeWhile id . zipWith (==) x) y
agreeLen x = length . takeWhile id . zipWith (==) x
Run Code Online (Sandbox Code Playgroud)

至于x,我们可以通过处理使用的处理它(.)组成zipWith (==) x与其他功能作为具有功能被修饰的值的另一种情况下:

agreeLen x = (.) (length . takeWhile id) (zipWith (==) x)
agreeLen x = ((length . takeWhile id) .) (zipWith (==) x) -- cosmetical change
agreeLen x = (((length . takeWhile id) .) . zipWith (==)) x
agreeLen = ((length . takeWhile id) .) . zipWith (==)
Run Code Online (Sandbox Code Playgroud)

这不是你在实践中真正想做的事情,但它肯定是可能的.

  • 我觉得没什么.您可以使用`RankNTypes`遇到不可信度. (2认同)

che*_*ner 6

您的第一个可以用无点样式编写,但您必须使其适合您map作为中缀函数的使用.

let lengthz = (length `map`) -- write the partial application as a section
Run Code Online (Sandbox Code Playgroud)

问题agreeLen在于zipWith它不是一个参数的函数,而是两个参数的函数.zipWith需要在将结果传递给它之前应用于两个参数takeWhile id.以无点样式编写它的不容易的方法是

-- via http://pointfree.io
agreeLen = ((length . takeWhile id) .) . zipWith (==)
Run Code Online (Sandbox Code Playgroud)

简单的讲,zipWith (==)适用于第一个参数x,以agreeLen产生一个新的功能(一个是获得一个列表,并返回一个压缩列表).这个新函数作为参数给出(length . takeWhile id) .,它产生一个新的组合函数,它接受第二个参数agreeLen并产生所需的Int值.

(@duplode可能派生这超过了我正要尝试干净.)

当初始函数需要超过2个参数时,一个快速失控的技巧是明确地解决它,进行组合,然后重新调整结果.

agreeLen = curry $ length . takeWhile id . (uncurry $ zipWith (==))
Run Code Online (Sandbox Code Playgroud)