let f = map tail.lines
f "fsdaf\nfdsf\n"
为什么会有效?
let f = map tail.tail.lines
f "fasdf\nfasdfdsfd\n"
我得到结果:
["asdfdsfd"]
let f = map (tail.tail).lines
f "fasdf\nfasdfdsfd\n"
我得到结果:
["sdf","sdfdsfd"]
我想知道haskell如何解析上面的代码。
答案 0 :(得分:6)
让我们看看你的第一个例子:
let f = map tail.tail.lines
f "fasdf\nfasdfdsfd\n"
首先,lines
将输入字符串分解为字符串数组:["fasdf","fasdfdsfd"]
。
现在,从右到左工作,tail
放弃" head"列表:["fasdfdsfd"]
最后,map tail
应用"尾部"列表中的每个元素:["asdfdsfd"]
你的第二个例子类似:
let f = map (tail.tail).lines
f "fasdf\nfasdfdsfd\n"
同样,您将输入字符串分解为字符串数组:["fasdf","fasdfdsfd"]
然而,现在,您正在创建复合函数(tail.tail)
(删除列表中的#34; head"两次),并将其映射到列表中的每个元素。
因此,您删除每个字符串的前两个字符。
["sdf","sdfdsfd"]
您的示例都按预期工作。阅读有关haskell中的关联性和复合函数的内容,以便更多地了解它。
编辑:区别在于:
map tail (tail lines)
vs map (tail.tail) lines
请记住,在Haskell中,函数是一等公民 - 您可以创建复合函数(例如:(+1).(+1)
)并执行其他不常见的操作(例如map
列表中的函数)用其他语言。
答案 1 :(得分:1)
第一篇文章是完全正确的,但是因为你找到了更多的问题,这是我的尝试......
当您尝试理解此代码的作用时,了解函数的作用以及它们的应用顺序非常重要,所以让我们来看看:
lines :: String -> [String] --takes a String and returns a list of Strings
tail :: [a] -> [a] --takes a (nonempty) list and drops the head (a list containing the rest)
(.) :: (b -> c) -> (a -> b) -> a -> c --function composition
现在这很重要...函数组合是正确的关联,优先级为9(最高!)
map :: (a -> b) -> [a] -> [b] --applies (a -> b) to every element of [a]
现在代码:
let f = map tail.tail.lines
相当于
let f x = map tail ((tail.lines) $ x)
这意味着您将tail
映射到tail
结果lines
的结果。这种行为是因为(.)
,以及函数应用程序在Haskell中是正确关联的。值得注意的是,(tail.lines)
是部分函数应用的结果(正如您可以从(。)的类型签名中看到a
缺失...因此它将返回一个函数需要a
并返回c
)
在后面的例子中:
let f = map (tail.tail).lines
应用程序的顺序由括号改变...此版本相当于:
let f x = map (tail.tail) (lines $ x)
所以它会将tail.tail
(放头两次)映射到lines
的结果上。
关键是理解相关性。它确定在没有括号的情况下首先应用哪个函数,以及函数是否具有相同的优先级。
我希望这有助于您了解其中的差异。