我尝试过练习Haskell的一系列例子。我目前正在学习继续传递,但是我对如何实现像找到列表中的元素索引的函数有点困惑:
index 3 [1,2,3] id = 2
像factorial这样的例子是有意义的,因为除了乘法之外没有真正的数据处理,但是在索引函数的情况下,我需要比较我看到的元素I元素我正在寻找,而我似乎无法弄清楚如何使用函数参数来做到这一点。
任何帮助都会很棒。
答案 0 :(得分:6)
首先让我告诉你一个可能的实现:
index :: Eq a => a -> [a] -> (Int -> Int) -> Int
index _ [] _ = error "not found"
index x (x':xs) cont
| x == x' = cont 0
| otherwise = index x xs (\ind -> cont $ ind + 1)
如果您更喜欢无点样式:
index :: Eq a => a -> [a] -> (Int -> Int) -> Int
index _ [] _ = error "not found"
index x (x':xs) cont
| x == x' = cont 0
| otherwise = index x xs (cont . (+1))
技巧是使用 continuations 来计算索引 - 这些延续将索引转到右边< / em>只是增加它。
如您所见,如果无法找到元素,则会导致错误。
λ> index 1 [1,2,3] id
0
λ> index 2 [1,2,3] id
1
λ> index 3 [1,2,3] id
2
λ> index 4 [1,2,3] id
*** Exception: not found
找出这样的东西的一个好方法是先用延续写下递归调用:
useCont a (x:xs) cont = useCont a xs (\valFromXs -> cont $ ??)
现在你必须考虑你想要的valFromXs
(作为类型和价值) - 但请记住你典型的开始(这里将是第一个延续 id
,因此类型只能是Int -> Int
。所以应该清楚我们在这里讨论的是索引转换。由于useCont
只会在下一次调用中知道尾部xs
,因此将此索引视为相对于xs
似乎很自然,其余部分应遵循很快。
IMO这只是
的另一个例子让类型引导你Luke
)
我不认为这是Haskell中 continuations 的典型用法。
一旦你可以使用累加器参数(这在概念上更简单):
index :: Eq a => a -> [a] -> Int -> Int
index _ [] _ = error "not found"
index x (x':xs) ind
| x == x' = ind
| otherwise = index x xs (ind+1)
或(see List.elemIndex)您可以使用Haskells laziness / list-comprehensions使其看起来更好:
index :: Eq a => a -> [a] -> Int
index x xs = head [ i | (x',i) <- zip xs [0..], x'== x ]
答案 1 :(得分:4)
如果您有一个值a
,那么要将其转换为CPS样式,您可以使用(a -> r) -> r
替换某些未指定的r
。在您的情况下,基本函数为index :: Eq a => a -> [a] -> Maybe Int
,因此CPS表单为
index :: Eq a => a -> [a] -> (Maybe Int -> r) -> r
甚至
index :: Eq a => a -> [a] -> (Int -> r) -> r -> r
让我们实施后者。
index x as success failure =
值得注意的是,有两个延续,一个是成功的结果,一个是失败的结果。我们将根据需要应用它们,并像往常一样引入列表的结构。首先,显然,如果as
列表为空,那么这是一个失败
case as of
[] -> failure
(a:as') -> ...
在成功案例中,正常情况下,我们是否对x == a
感兴趣。当它成立时我们将成功延续传递给索引0
,因为毕竟我们在输入列表的0
索引处找到了匹配。
case as of
...
(a:as') | x == a -> success 0
| otherwise -> ...
那么当我们还没有匹配时会发生什么?如果我们以未改变的方式传递成功延续,则假设找到匹配,最终将以0
作为参数调用。但是,这已经失去了关于我们已经试图一次调用它的事实的信息。我们可以通过修改延续来纠正这个问题
case as of
...
(a:as') ...
| otherwise -> index x as' (fun idx -> success (idx + 1)) failure
考虑它的另一种方式是我们有收集&#34; post&#34;延续中的动作,因为最终计算结果将通过该代码
-- looking for the value 5, we begin by recursing
1 :
2 :
3 :
4 :
5 : _ -- match at index 0; push it through the continuation
0 -- lines from here down live in the continuation
+1
+1
+1
+1
如果我们以无点样式
编写递归分支,这可能会更加清晰 | otherwise -> index x as' (success . (+1)) failure
显示我们如何修改延续以包含每个递归调用的一个增量。所有代码都是
index :: Eq a => a -> [a] -> (Int -> r) -> r -> r
index x as success failure
case as of
[] -> failure
(a:as') | x == a -> success 0
| otherwise -> index x as' (success . (+1)) failure