2

我是 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

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

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

Prelude> find2 2 [(1,11),(2,22)]
11

然后我很惊讶地看到这个值2实际上与1. 为了确保我不希望不可能的事情发生,我还尝试了以下操作,以确认在 Haskell 中可以进行部分匹配,看起来确实如此:

Prelude> head [v | (2,v) <- [(1,11),(2,22)]]
22

我还注意到函数声明的不同。我添加了所需的信息,因此两者的声明find看起来find2完全一样。但结果仍然是损坏的(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

怎么可能2匹配1

(1)以上函数来自优秀书籍《Programming in Haskell》第93页

4

2 回答 2

11

是的,Haskell 模式匹配与 Prolog 模式匹配有着根本的不同。

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

let x = 5 in case (1,2) of (x,y) -> "matched!"   -- gives "matched!"

将始终评估为“匹配!”。这是因为xin(x,y)新绑定到1,而不是与“现有”的值相比,外部定义x,如您在此处看到的:

let x = 5 in case (1,2) of (x,y) -> x     -- gives "1"

数值常量的行为不同:

case (1,2) of (5,y) -> "matched!"    -- match fails

对于其他构造函数:

case (True,2) of (False,y) -> "match!"   -- match fails

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

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

let x = 5
f x = "hi"     -- defines `f` for any `x`, not just `f 5`

或像您的示例一样列出理解。在表达式中:

[v | (k,v) <- [(1,2),(3,4)]]    -- gives [(1,2),(3,4)]

变量k并且v将始终是新鲜的,因此将绑定到任何元组,尽管kor有任何外部的现有定义v-Wall如果您使用(特别是)打开警告-Wname-shadowing,这将提醒您注意阴影绑定。如果k用常量(或其他构造函数)替换,它的行为会有所不同:

[v | (3,v) <- [(1,2),(3,4)]]    -- only gives [(3,4)]

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

于 2017-10-07T17:53:58.180 回答
0

谢谢你的帮助!经过一番搜索,我还发现这个答案很有帮助:How can I re-assign a variable in a function in Haskell?

创建一个具有相同名称的新变量,它会遮盖前一个变量。但是第一个变量继续存在,并且在某些情况下仍然可以访问......

因此,这确实与 Prolog 相去甚远,是的,以下标志非常有用:

Prelude> :set -fwarn-name-shadowing 
于 2017-10-08T04:51:39.550 回答