Haskell元组与函数参数不匹配

lin*_*nse 2 haskell pattern-matching ghci winghci

我是Haskell的新手,所以它可能是显而易见的,但我做了广泛的Prolog,所以我对这一个感到困惑......

使用GHCi时,我创建了以下函数(1):

Prelude> let find k t = head [v | (k',v) <- t, k == k'] -- Definiton of find
find :: Eq a1 => a1 -> [(a1, a)] -> a

Prelude> find 2 [(1,11),(2,22)] -- Invocation of find
22
Run Code Online (Sandbox Code Playgroud)

这是预期的.然后我尝试从定义中删除k':

Prelude> let find2 k t = head [v | (k,v) <- t]
find2 :: t -> [(t1, a)] -> a

Prelude> find2 2 [(1,11),(2,22)]
11
Run Code Online (Sandbox Code Playgroud)

然后我很惊讶地发现这个价值2与之相符1.为了确保我不希望不可能,我也尝试以下内容以确认在Haskell中可以进行部分匹配,看起来实际情况如下:

Prelude> head [v | (2,v) <- [(1,11),(2,22)]]
22
Run Code Online (Sandbox Code Playgroud)

我还注意到函数声明的不同之处.我添加了所需的信息,因此声明findfind2看起来完全相同.但结果仍然是打破(2,_)matchnig (1,11):

Prelude> let find2 :: Eq a1 => a1 -> [(a1, a)] -> a; find2 k t = head [v | (k,v) <- t]
find2 :: Eq a1 => a1 -> [(a1, a)] -> a

Prelude> find2 2 [(1,11),(2,22)]
11
Run Code Online (Sandbox Code Playgroud)

怎么可以2匹配1任何意思?

(1)上述功能来自优秀的"Haskell编程"第93页

K. *_*uhr 11

是的,Haskell模式匹配与Prolog模式匹配根本不同.

在Haskell中,模式中的变量指的是将由匹配绑定的新变量,而不是必须匹配的现有变量.所以,表达式:

let x = 5 in case (1,2) of (x,y) -> "matched!"   -- gives "matched!"
Run Code Online (Sandbox Code Playgroud)

将始终评估为"匹配!".这是因为xin (x,y)新获得了1,而不是与"现有"的外部定义的值相比x,正如你在这里看到的那样:

let x = 5 in case (1,2) of (x,y) -> x     -- gives "1"
Run Code Online (Sandbox Code Playgroud)

数字常量的行为是不同的:

case (1,2) of (5,y) -> "matched!"    -- match fails
Run Code Online (Sandbox Code Playgroud)

以及其他构造函数:

case (True,2) of (False,y) -> "match!"   -- match fails
Run Code Online (Sandbox Code Playgroud)

这不是"重新绑定",而是必须匹配模式匹配才能成功.这是字母数字构造函数以大写字母开头的众多原因之一:否则,确定模式是否涉及与现有构造函数匹配或重新绑定到新变量将非常困难.

这适用于任何上下文中的模式匹配,无论是上面的case表达式还是像这样的函数定义:

let x = 5
f x = "hi"     -- defines `f` for any `x`, not just `f 5`
Run Code Online (Sandbox Code Playgroud)

或列出你的例子的理解.在表达式中:

[v | (k,v) <- [(1,2),(3,4)]]    -- gives [(1,2),(3,4)]
Run Code Online (Sandbox Code Playgroud)

变量kv永远是新鲜的,所以会绑定到任何一个元组,尽管任何外,现有的定义kv.如果您使用-Wall(特别-Wname-shadowing)打开警告,这将提醒您阴影绑定.如果k用常量(或其他构造函数)替换它,它的行为会有所不同:

[v | (3,v) <- [(1,2),(3,4)]]    -- only gives [(3,4)]
Run Code Online (Sandbox Code Playgroud)

你可能不喜欢它,但这就是Haskell的工作方式.