跨度函数定义如下。我很好奇(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'
答案 0 :(得分:1)
许多模式语法也可以用于表达式,因此您可以使用相同的语法来分割带有模式的数据,就像用于使用表达式构建数据一样。
请注意,由于Haskell值是不可变的,因此 没有就地替换。
部分(x:ys,zs)
本身不是模式,而是一个从值x
,ys
和zs
构建新值的表达式,它们本身来自模式。
x
来自模式xs@(x:xs')
,并且绑定到作为span
的第二个参数传递的列表的第一个元素。这也会将xs'
绑定到列表的余数,并将xs
绑定到原始整体。 (@
表示“将模式匹配到右边但也给出一个绑定到整体的名称,并且模式也可以用作表达式的规则例外。”< / p>
ys
和zs
来自(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
已知(它被绑定为模式匹配的一部分),但ys
和zs
尚未计算 - 只是它们的定义与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