在 Haskell 中执行 where 子句

Execution of where clause in Haskell

我得到了以下我知道有效的代码片段,但我对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 个参数:

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

如您所见,除了基本情况外,我还有 2 个情况 (2) 和 (3),其中 status/[Int] 参数通过 标准集合谓词 .

问题 1) :

问题 2) :

作为惰性求值的结果,如果在对匹配的情况。所以:

  • 如果情况 1 成立,第 7-9 行的 none 是 运行。
  • 如果情况 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 子句版本没有任何问题,并且编写 Haskell 代码的情况并不少见,其中只有 where 中绑定的子集每种情况都使用 -clause(甚至 valid/meaningful)。有了这么小的助手,这个具体的例子可能会在没有任何助手的情况下写得更清楚:

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,所有这四种(您的原始代码和这三种变体)都可以编译为相同的低级代码,因此请使用您认为最清楚的一种。