haskell中执行序列的“情况”很奇怪

时间:2019-03-24 11:23:17

标签: haskell

我正在编写一个函数,用于从Haskell的列表中过滤第一个合格值。

我尝试了基于过滤器功能的功能,但是我不知道该功能如何运行。

所以我为每种情况添加了跟踪。

filter' :: (a -> Bool) -> [a] -> [a]
filter' p (x:xs) | p x       = trace "{1}" x : (filter' p xs)
                 | otherwise = trace "{2}" filter' p xs
filter' p [] = []


filterFirst :: (a->Bool) -> [a]->[a]
filterFirst p xs = case (trace "{3}" (filter' p xs)) of 
  (x : xs) -> trace "{4}" [x]
  otherwise -> trace "{5}" []

测试时:

filterFirst even [2,2]

{3}
{4}
[{1}
2]

filterFirst even [1,2]

{3}
{2}
{4}
[{1}
2]

filterFirst even [1,1,2]

{3}
{2}
{2}
{4}
[{1}
2]

结果[2]是正确的,但是执行顺序已连接。

  1. 为什么{4}在{1}之前执行?
  2. {3}似乎只执行一次,为什么case of知道何时测试(x : xs)
  3. case of如何准确执行?

2 个答案:

答案 0 :(得分:3)

trace "{1}" x : (filter' p xs)

解析为

(trace "{1}" x) : (filter' p xs)

类似地,

trace "{2}" filter' p xs

解析为

(trace "{2}" filter') p xs

如果需要其他解释,则需要分别编写trace "{1}" (x : (filter' p xs))trace "{2}" (filter' p xs)


我不确定您的问题3。 {3}为什么要执行多次?它不是循环/递归的一部分。

让我们逐步评估

filterFirst even [2,2]

解释器要打印结果,所以我们必须计算结果。

我们输入filterFirst的定义(始终首先对表达式的外部部分求值),

case (trace "{3}" (filter' p xs)) of 
  (x : xs) -> trace "{4}" [x]
  otherwise -> trace "{5}" []

where
    p = even
    xs = [2,2]

在这里我们需要做出决定。 case需要知道要评估的分支,因此需要知道要匹配的模式(x : xsotherwise)。因此,它要做的第一件事就是评估我们正在分析的表达式:

trace "{3}" (filter' p xs)

where
    p = even
    xs = [2,2]

最外面的表达式是对trace的调用,该调用现在显示{3}并移交给

filter' p xs

where
    p = even
    xs = [2,2]

现在最外面的表达式是对filter'的调用,该调用定义为

filter' p (x:xs) | p x       = trace "{1}" x : (filter' p xs)
                 | otherwise = trace "{2}" filter' p xs
filter' p [] = []

我们必须再次做出决定:我们使用哪个方程式?选择取决于第二个参数的值,因此这会触发对xs = [2,2](最外层)的求值。好吧,这里没有什么有趣的事情,因为[2,2]已经得到了充分评估。

[2,2]2 : (2 : [])的语法糖,因此它与第一个等式绑定绑定

p = even
x = 2
xs = 2 : []  -- same as [2]

我们现在必须评估模式背后的警卫:

p x
even 2
True

成功!我们的返回值为trace "{1}" x : (filter' p xs),(如上所述)是(:)对两个参数trace "{1}" xfilter' p xs的应用。

这里没有进一步的评估。回想一下,我们仍在尝试评估

case ... of 
  (x : xs) -> trace "{4}" [x]
  otherwise -> trace "{5}" []

因此,我们只关心列表的最外面的构造函数是:还是[],现在知道它是:。我们选择第一个分支,它产生

trace "{4}" [x]

where
    x = trace "{1}" x_inner
    xs = filter' p xs_inner
    x_inner = 2
    xs_inner = 2 : []
    p = even

这有点令人困惑,因为您已将所有变量命名为xxs。如果我们内联所有我们已经知道其值的变量,则绑定将减少为:

trace "{4}" [x]

where
    x = trace "{1}" 2
    xs = filter' even (2 : [])

xs完全不使用,x仅使用一次,因此它实际上等同于

trace "{4}" [trace "{1}" 2]

首先评估{4}(来自trace),然后得出结果

[trace "{1}" 2]
-- syntactic sugar for
(trace "{1}" 2) : []

列表打印代码在这里开始工作:我们已经有了至少一个元素的列表,因此它输出[(列表的开始)。要打印第一个元素本身,我们需要对其进行评估:

trace "{1}" 2

这最终将打印{1}并产生2,这将使列表打印代码输出2](对于列表的末尾)。

就是这样!

答案 1 :(得分:0)

我没有检查所有内容,但请注意

trace "{1}" x : (filter' p xs)

表示

(trace "{1}" x) : (filter' p xs)

因此,{1}将在您访问第一个元素内容时打印,而不是仅在生成它时打印。我想你想要

trace "{1}" (x : (filter' p xs))

我也将{2}行改写为

trace "{2}" (filter' p xs)