Haskell中where子句的执行

我得到了以下代码,我知道它可以工作,但我对 Haskell 完全陌生,并且有 2 个关于 where 子句的问题。

f3 :: [[Int]] -> [Int] -> [Int]
f3 [] status = status --- Base Case
f3 ([p1,p2]:tail) status
 | status !! (p1-1) == 0  = f3 tail status  --- Case 1
 | status !! (p2-1) == 1  = f3 tail newStatus1 --- Case 2
 | otherwise = f3 tail newStatus2 --- Case 3
  where newStatus1 = set status p1 0                    --- Line 7
        newStatus2 = set newStatus2Temp p1 1            --- Line 8
        newStatus2Temp = set status p2 0                --- Line 9

所以基本上 f3 谓词有 2 个参数:

  • 像这样的整数列表列表:[[1,2],[2,3],[3,2]]
  • 整数列表

它的输出是最终更新的第二个参数。

正如您所看到的,除了基本情况之外,我还得到了 2 种情况 (2) 和 (3),其中 status/[Int] 参数通过标准 set predicate

问题 1):

  • 说案例2是真的。Haskell 执行第 8 行和第 9 行吗?
  • 说案例3是真的。Haskell 执行第 7 行吗?

问题2) :

  • 守卫可以拥有自己的位置吗?
  • 有没有更好的方法来实际做到这一点?

回答

作为惰性求值的结果,第 7-9 行中的每一行中的代码仅在对匹配情况的代码求值过程中评估/使用相应绑定的值时才运行。所以:

  • 如果情况 1 为真,则第 7-9 行都不会运行。
  • 如果情况 1 为假但情况 2 为真,则评估newStatus运行第 7 行,但不运行第 8-9 行。
  • 如果情况 1 和 2 为假但情况 3 为真,则newStatus2运行第 8 行的评估,该评估newStatus2Temp导致第 9 行运行。第 7 行未运行。

where子句本身只能附着到整个图案绑定(例如,整个f3 ([p1,p2]:tail) status | ... | ... = ...表达),而不是单个防护装置,以便保护不能有其自己的where条款。您可以为每个守卫重复该模式:

f3 :: [[Int]] -> [Int] -> [Int]
f3 [] status = status
f3 ([p1,p2]:tail) status | status !! (p1-1) == 0  = f3 tail status
f3 ([p1,p2]:tail) status | status !! (p2-1) == 1  = f3 tail newStatus1
  where newStatus1 = set status p1 0
f3 ([p1,p2]:tail) status | otherwise              = f3 tail newStatus2
  where newStatus2 = set newStatus2Temp p1 1
        newStatus2Temp = set status p2 0

或使用let ... in ...块:

f3 :: [[Int]] -> [Int] -> [Int]
f3 [] status = status
f3 ([p1,p2]:tail) status
 | status !! (p1-1) == 0  = f3 tail status
 | status !! (p2-1) == 1
  = let newStatus1 = set status p1 0
    in f3 tail newStatus1
 | otherwise
  = let newStatus2 = set newStatus2Temp p1 1
        newStatus2Temp = set status p2 0
    in f3 tail newStatus2

我认为您的where-clause 版本没有任何问题,并且编写 Haskell 代码并在where每种情况下仅使用 -clause 中的绑定子集(甚至有效/有意义)的情况并不少见。有了这么小的助手,这个特定的例子可能在没有任何助手的情况下写得更清楚:

f3 :: [[Int]] -> [Int] -> [Int]
f3 [] status = status
f3 ([p1,p2]:tail) status
 | status !! (p1-1) == 0  = f3 tail $ status
 | status !! (p2-1) == 1  = f3 tail $ set status p1 0
 | otherwise              = f3 tail $ set (set status p2 0) p1 1

使用 GHC 和-O2,所有这四个(您的原始代码和这三个变体)都编译为相同的低级代码,因此请使用您认为最清楚的那个。


以上是Haskell中where子句的执行的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>