我一直对学习新语言很感兴趣,这一事实使我始终不渝,并使我(我相信)成为一名更好的程序员。我征服Haskell的尝试来来去去-到目前为止两次-我决定是时候再试一次。第三次是魅力,对不对?
不。我重新阅读了我的旧笔记...并感到失望:-(
上次使我失去信心的问题是一个简单的问题:整数的排列。
即从整数列表到列表列表-排列列表:
[int] -> [[int]]
实际上,这是一个通用问题,因此仍将上面的“int”替换为“a”。
从我的笔记:
我先自己编写代码,成功。欢呼!
我将解决方案发送给我的一个好 friend -Haskell大师,这通常有助于向大师学习。他告诉我,这告诉我,“表达了语言的真正力量,使用通用工具为您编写代码需求”。所有这些,我最近喝了kool-aid,让我们开始吧:
permute :: [a] -> [[a]]
permute = foldr (concatMap.ins) [[]]
where ins x [] = [[x]]
ins x (y:ys) = (x:y:ys):[ y:res | res <- ins x ys]
嗯
让我们分解一下:
bash$ cat b.hs
ins x [] = [[x]]
ins x (y:ys) = (x:y:ys):[ y:res | res <- ins x ys]
bash$ ghci
Prelude> :load b.hs
[1 of 1] Compiling Main ( b.hs, interpreted )
Ok, modules loaded: Main.
*Main> ins 1 [2,3]
[[1,2,3],[2,1,3],[2,3,1]]
好,到目前为止,很好。花了我一点时间来理解“ins”的第二行,但是可以:
它将第一个arg放在列表中所有可能的位置。凉。
现在,了解foldr和concatMap。在“现实世界的Haskell”中,DOT进行了解释...
(f . g) x
...只是...的另一种语法
f (g x)
在上师发送的代码中,文件夹使用了DOT,其中“ins”函数用作折叠“collapse”:
*Main> let g=concatMap . ins
*Main> g 1 [[2,3]]
[[1,2,3],[2,1,3],[2,3,1]]
好的,因为我想了解大师如何使用DOT,所以我根据DOT定义尝试等效表达式(f。g)x = f(g x)...
*Main> concatMap (ins 1 [[2,3]])
<interactive>:1:11:
Couldn't match expected type `a -> [b]'
against inferred type `[[[t]]]'
In the first argument of `concatMap', namely `(ins 1 [[2, 3]])'
In the expression: concatMap (ins 1 [[2, 3]])
In the definition of `it': it = concatMap (ins 1 [[2, 3]])
什么!?!为什么?
好的,我检查了concatMap的签名,发现它需要一个lambda和一个列表,但这就是
只是人类的思想; GHC如何应对?根据上面DOT的定义...
(f.g)x = f(g x),
...我所做的是有效的,明智地替换:
(concatMap . ins) x y = concatMap (ins x y)
抓头...
*Main> concatMap (ins 1) [[2,3]]
[[1,2,3],[2,1,3],[2,3,1]]
所以... DOT的解释显然是
太简单了... DOT必须足够聪明才能理解
我们实际上是想让“ins”被诅咒并“吃掉”第一个
自变量-从而成为只想对[t]进行操作的函数
(并在所有可能的位置将它们“散布”为“1”)。
但是在哪里指定的呢?当我们调用以下命令时,GHC如何知道如何做到这一点:
*Main> (concatMap . ins) 1 [[2,3]]
[[1,2,3],[2,1,3],[2,3,1]]
“ins”签名是否以某种方式传达了……“吃我的第一个论点”政策?
*Main> :info ins
ins :: t -> [t] -> [[t]] -- Defined at b.hs:1:0-2
我没什么特别的-“ins”是一个带有“t”的函数,
“t”的列表,然后继续创建包含所有“interspersals”的列表。什么都没有“吃掉你的第一个论点并消除它”。
所以在那里...我感到困惑。我了解了(经过一个小时的查看代码!),发生了什么,但是...万能的上帝...也许GHC试图尝试查看可以“剥离”多少个参数?
let's try with no argument "curried" into "ins",
oh gosh, boom,
let's try with one argument "curried" into "ins",
yep, works,
that must be it, proceed)
再次-...
而且由于我总是将自己正在学习的语言与已经知道的语言进行比较,因此“ins”在Python中的外观如何?
a=[2,3]
print [a[:x]+[1]+a[x:] for x in xrange(len(a)+1)]
[[1, 2, 3], [2, 1, 3], [2, 3, 1]]
老实说,现在...哪个更简单?
我的意思是,我知道我是Haskell的新手,但是我感觉像个白痴……看了4行代码一个小时,最后假设编译器...尝试了各种解释,直到找到“点击”?
引用致命武器说:“我为此太老了……”
最佳答案
(f . g) x = f (g x)
这是真的。你从那得出结论
(f . g) x y = f (g x y)
也必须是真实的,但事实并非如此。实际上,以下是正确的:
(f . g) x y = f (g x) y
这是不一样的。
为什么会这样呢?好吧
(f . g) x y
与((f . g) x) y
相同,并且由于我们知道(f . g) x = f (g x)
,我们可以将其简化为(f (g x)) y
,这又与f (g x) y
相同。因此
(concatMap . ins) 1 [[2,3]]
等同于concatMap (ins 1) [[2,3]]
。这里没有魔术。解决此问题的另一种方法是通过类型:
.
的类型为(b -> c) -> (a -> b) -> a -> c
,concatMap
的类型为(x -> [y]) -> [x] -> [y]
,ins
的类型为t -> [t] -> [[t]]
。因此,如果我们使用concatMap
作为b -> c
参数,并且使用ins
作为a -> b
参数,则a
成为t
,b
成为[t] -> [[t]]
和c
成为[[t]] -> [[t]]
(x
= [t]
和y
= [t]
)因此,
concatMap . ins
的类型为t -> [[t]] -> [[t]]
,这意味着一个函数接受任意值和列表列表(任意值)并返回列表列表(相同类型)。关于haskell - Haskell函数的应用和currying,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/3884121/