在没有 <<loop>> 的情况下修补递归定义的列表
Patching a recursively-defined list without a <<loop>>
上下文
我们都知道the recursively-defined Fibonacci sequence:
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
λ> fibs
[1,1,2,3,5,9,13,21,34,55,89...
问题
我正在尝试在几个地方“修补”它,以便:
- 一般递归方程“元素是前两个元素之和”成立,但是
- 关于单个元素的值可以有可数的例外。
我在哪里
实用程序
为此,我将定义以下函数来修改列表中的特定元素:
patch :: Int -> a -> [a] -> [a]
patch i v xs = left ++ v : right where (left,_:right) = splitAt i xs
我可以用它来改变自然数的顺序:
λ> patch 5 0 [0..]
[0,1,2,3,4,0,6,7,8,9...
Post-补丁
到目前为止,还不错。现在修补斐波那契数列:
λ> patch 1 0 fibs
[1,0,2,3,5,8,13,21,34,55,89...
这满足要求 (2)。
完整补丁
为了同样得到 (1),我将以更明确的打结方式重写定义:
fibs' p = rec where rec = p (1 : 1 : zipWith (+) rec (tail rec))
没有补丁,它仍然可以正常工作:
λ> fibs' id
[1,1,2,3,5,9,13,21,34,55,89...
我现在可以修补我想要的元素并保持递归定义:
λ> fibs' (patch 1 0)
[1,0,1,1,2,3,5,8,13,21,34...
限制
但是我可以吗?
λ> fibs' (patch 5 0)
<<loop>>
问题
怎么了?
直觉上,数据流似乎是合理的。每个列表元素都应该有一个不涉及循环的正确定义。我的意思是,对于无补丁来说已经足够好了 fibs
;补丁只应使其 更多 定义。
所以我可能遗漏了什么。我的 patch
函数存在一些严格性问题?其他地方的一些严格问题?完全是别的东西?
你比你想的要严格一些。看看
patch i v xs = left ++ v : right where (left,_:right) = splitAt i xs
我相信您打算 xs
保证至少有 i
个元素。但是 splitAt
不知道。您可能可以使用自己的拆分器修复您的程序。
splitAtGuaranteed :: Int -> [a] -> ([a], [a])
splitAtGuaranteed 0 xs = ([], xs)
splitAtGuaranteed n ~(x:xs) = first (x :) $ splitAtGuaranteed (n - 1) xs
编辑
Daniel Wagner 指出您不需要 splitAtGuaranteed
的所有懒惰(或偏心)。稍微懒一点就够了:
patch i v xs = left ++ [v] ++ drop 1 right where (left, right) = splitAt i xs
上下文
我们都知道the recursively-defined Fibonacci sequence:
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
λ> fibs
[1,1,2,3,5,9,13,21,34,55,89...
问题
我正在尝试在几个地方“修补”它,以便:
- 一般递归方程“元素是前两个元素之和”成立,但是
- 关于单个元素的值可以有可数的例外。
我在哪里
实用程序
为此,我将定义以下函数来修改列表中的特定元素:
patch :: Int -> a -> [a] -> [a]
patch i v xs = left ++ v : right where (left,_:right) = splitAt i xs
我可以用它来改变自然数的顺序:
λ> patch 5 0 [0..]
[0,1,2,3,4,0,6,7,8,9...
Post-补丁
到目前为止,还不错。现在修补斐波那契数列:
λ> patch 1 0 fibs
[1,0,2,3,5,8,13,21,34,55,89...
这满足要求 (2)。
完整补丁
为了同样得到 (1),我将以更明确的打结方式重写定义:
fibs' p = rec where rec = p (1 : 1 : zipWith (+) rec (tail rec))
没有补丁,它仍然可以正常工作:
λ> fibs' id
[1,1,2,3,5,9,13,21,34,55,89...
我现在可以修补我想要的元素并保持递归定义:
λ> fibs' (patch 1 0)
[1,0,1,1,2,3,5,8,13,21,34...
限制
但是我可以吗?
λ> fibs' (patch 5 0)
<<loop>>
问题
怎么了?
直觉上,数据流似乎是合理的。每个列表元素都应该有一个不涉及循环的正确定义。我的意思是,对于无补丁来说已经足够好了 fibs
;补丁只应使其 更多 定义。
所以我可能遗漏了什么。我的 patch
函数存在一些严格性问题?其他地方的一些严格问题?完全是别的东西?
你比你想的要严格一些。看看
patch i v xs = left ++ v : right where (left,_:right) = splitAt i xs
我相信您打算 xs
保证至少有 i
个元素。但是 splitAt
不知道。您可能可以使用自己的拆分器修复您的程序。
splitAtGuaranteed :: Int -> [a] -> ([a], [a])
splitAtGuaranteed 0 xs = ([], xs)
splitAtGuaranteed n ~(x:xs) = first (x :) $ splitAtGuaranteed (n - 1) xs
编辑
Daniel Wagner 指出您不需要 splitAtGuaranteed
的所有懒惰(或偏心)。稍微懒一点就够了:
patch i v xs = left ++ [v] ++ drop 1 right where (left, right) = splitAt i xs