高阶lambda函数中的变量范围

时间:2015-10-06 14:43:28

标签: haskell lambda scope anonymous-function

在解决8皇后区问题的过程中,一个人使用了以下代码行:

sameDiag try qs = any (\(colDist,q) -> abs (try - q) == colDist) $ zip [1..] qs

try是一个项目; qs是相同项目的列表。

  1. 有人可以解释lambda函数中的colDistq如何绑定到任何东西?

  2. lambda函数体中使用的tryq如何进入同一范围?

  3. 在某种程度上,这是一个Haskell习语,这种设计方法有什么问题有助于解决?

2 个答案:

答案 0 :(得分:0)

zip :: [a] -> [b] -> [(a,b)]获取两个列表并将它们成对加入,最后删除剩余的列表。

any :: (a -> Bool) -> [a] -> Bool接受一个函数和a的列表,然后如果任何值返回true,则返回True

因此colDistq是由zip [1..] qs生成的列表中对的第一个和第二个元素,并且当它们应用于{{1 }}

any仅绑定在lambda函数的主体内 - 这与lambda演算相同。由于q之前已在函数定义中绑定,因此它仍可在此内部范围中使用。如果你想到lambda演算,那么术语try是有道理的,尽管\x.\y.x+yx在不同的时间被绑定。

至于设计方法,这种方法比尝试手动迭代或递归列表要简洁得多。它对我的意图似乎很清楚(关于它来自更大的代码库)。

答案 1 :(得分:0)

函数 any 是一个higher-order function,它有两个参数:

  • 第一个参数的类型为a -> Bool,即从aBool的函数
  • 第二个参数属于[a]类型,即类型为a的项目列表;

即。 第一个参数是一个函数,它将列表中的任何元素作为第二个参数传递,并根据该元素返回Bool。 (它可以采用a类型的任何值,而不仅仅是该列表中的值,但显然可以肯定any不会使用a的任意值调用它但列表中的那些。)

然后,您可以通过轻微的重构来简化对原始代码段的思考:

sameDiag :: Int -> [Int] -> Bool
sameDiag try qs = any f xs
  where
    xs = zip [1..] qs
    f = (\(colDist, q) -> abs (try - q) == colDist)

可以转化为

sameDiag :: Int -> [Int] -> Bool
sameDiag try qs = any f xs
  where
    xs = zip [1..] qs
    f (colDist, q) = abs (try - q) == colDist)

反过来可以转化为

sameDiag :: Int -> [Int] -> Bool
sameDiag try qs = any f xs
  where
    xs = zip [1..] qs
    f pair = abs (try - q) == colDist) where (colDist, q) = pair

(请注意,sameDiag也可以使用更通用的类型Integral a => a -> [a] -> Bool,而不是当前的单态类型

- 那么pair中的f pair = ...如何绑定到某个值?好吧,简单:它只是一个功能;无论谁调用它都必须传递pair参数的值。 - 在第一个参数设置为any的情况下调用f时,调用any的函数f的调用与列表中的各个元素{{} { 1}}作为参数xs的值传入。

并且,由于pair的内容是对的列表,因此可以将此列表中的单个对传递给xs,因为f期望它只是那个。 / p>

编辑:f的进一步解释,以解决提问者的评论:

  

这是一个公平的综合吗?这种设计高阶函数的方法允许调用代码改变f的行为方式并且在用于为列表中的每个元素调用f之前需要额外处理的列表调用高阶函数。封装列表处理(在这种情况下使用zip)似乎是正确的做法,但这个额外处理的目的是否真的在上面的原始单行中清楚了?

在调用any之前,any确实没有额外的处理。除了简单地遍历传入的列表f之外,还有非常简约的簿记:在迭代期间对元素调用xs,并立即打破迭代并在第一次返回f True为任何列表元素返回f

True的大部分行为都是“隐含的”,但它由Haskell的懒惰评估,基本语言语义以及any组成的现有函数来处理(嗯至少我的下面的版本,any - 我还没看过内置的any' Preludeany但是我确定它并没有太大的不同;只是可能更加优化)。

事实上,any很简单,在GHCi提示符下使用单行代码重新实现它几乎是微不足道的:

Prelude> let any' f xs = or (map f xs)

现在让我们看看GHC计算的类型:

Prelude> :t any'
any' :: (a -> Bool) -> [a] -> Bool

- 与内置any相同。那么让我们试试吧:

Prelude> any' odd [1, 2, 3]  -- any odd values in the list?
True
Prelude> any' even [1, 3]    -- any even ones?
False
Prelude> let adult = (>=18)
Prelude> any' adult [17, 17, 16, 15, 17, 18]

- 看看你如何有时编写几乎看起来像英语的高阶函数的代码?