跨度和模式匹配

时间:2014-08-24 14:02:19

标签: haskell pattern-matching

跨度函数定义如下。我很好奇(ys,zs)如何与(x:ys,zs)模式匹配,其中已经存在' x'和利弊。我有些人认为模式匹配会是一个就地替代品,但是这让我感到震惊,我的下巴已经下降了。这真的很美。

我很好奇是否在任何一本书中都解释了这个结构和更多内容(我现在正在阅读Real World Haskell第4章,并想知道这本书或其他任何内容是否详细解释了这一点)。对不起,如果我天真地离开,但对我来说,这是一个很好的模式匹配构造,我很想知道更多。

span p []            = ([],[])
span p xs@(x:xs') 
            | p x       =  (x:ys,zs)
            | otherwise =  ([],xs)
                           where (ys,zs) = span p xs'

2 个答案:

答案 0 :(得分:1)

许多模式语法也可以用于表达式,因此您可以使用相同的语法来分割带有模式的数据,就像用于使用表达式构建数据一样。

请注意,由于Haskell值是不可变的,因此 没有就地替换。 部分(x:ys,zs)本身不是模式,而是一个从值xyszs构建值的表达式,它们本身来自模式。

x来自模式xs@(x:xs'),并且绑定到作为span的第二个参数传递的列表的第一个元素。这也会将xs'绑定到列表的余数,并将xs绑定到原始整体。 (@表示“将模式匹配到右边但给出一个绑定到整体的名称,并且模式也可以用作表达式的规则例外。”< / p>

yszs来自(ys,zs)中的where (ys,zs) = span p xs'模式。它们绑定到span p xs'递归调用返回的元组的第一个和第二个元素,其中x被删除后列表的其余部分。

将这些组合在一起,表达式(x:ys,zs)生成一个与递归span p xs'返回的元组相同的元组,除了x已被强制转换为第一个元组元素。

其他人将不得不回答有关书籍的问题,我很久以前就学会了Haskell来阅读它们。但如果其他一切都失败了,你可以阅读precise definitions in the Haskell report

答案 1 :(得分:1)

你是对的,这个 很漂亮。在Haskell中,它与Prolog的TRMC最接近。

让我解释一下。该定义相当于

span p xs = case xs of 
             (x:t) | p x -> let (ys,zs) = span p t in
                            (x:ys,zs)   -- value1
             _           -> ([],xs)     -- value2 constructed from known parts

因为Haskell是 lazy ,所以value1被构造并立即返回,没有任何中间递归调用,就像简单的value2一样。此时x已知(它被绑定为模式匹配的一部分),但yszs尚未计算 - 只是它们的定义与value1一起保留其中有两个“洞”(x:_,_)。仅当稍后要求任何“空洞”值时,才会通过进一步调用span并使用结构化结果填充这些空洞来计算它们的值(let绑定也是模式匹配)

这在Haskell中称为保护递归 - 递归调用由构造函数保护 - 这里,(,)(:) - 创建带孔的值,待填充稍后根据需要。

顺便说一句,在Prolog中,这是写成

span(P,[], [],[]).       % -- two "inputs", two "outputs"
span(P,XS, A,B):- 
  XS = [X|T],
  ( call(P,X) ->         % -- predicate P holds for X:
      A=[X|YS], B=ZS,    % --   first, the value with holes is created
      span(P,T, YS,ZS)   % --   then, the holes are filled
    ;                    % -- else:
      A=[], B=XS ).      % --   output values are set