Haskell继续传递列表中元素的样式索引

时间:2015-04-14 04:03:09

标签: haskell functional-programming continuation-passing

我尝试过练习Haskell的一系列例子。我目前正在学习继续传递,但是我对如何实现像找到列表中的元素索引的函数有点困惑:

index 3 [1,2,3] id = 2

像factorial这样的例子是有意义的,因为除了乘法之外没有真正的数据处理,但是在索引函数的情况下,我需要比较我看到的元素I元素我正在寻找,而我似乎无法弄清楚如何使用函数参数来做到这一点。

任何帮助都会很棒。

2 个答案:

答案 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