【问题标题】:Find the K'th element of a list using foldr使用 foldr 查找列表的第 K 个元素
【发布时间】:2015-04-02 00:23:26
【问题描述】:

我尝试通过列表中的索引来实现自己的安全搜索元素。 我认为,我的函数必须有这个签名:

safe_search :: [a] -> Int -> Maybe a
safe_search xs n = foldr iteration init_val xs n
iteration = undefined
init_val = undefined

我对迭代的实现有疑问。我认为,它必须看起来像这样:

safe_search :: [a] -> Int -> Maybe a
safe_search xs n = foldr iteration init_val xs n
    where
    iteration :: a -> (Int -> [a]) -> Int -> a
    iteration x g 0 = []
    iteration x g n = x (n - 1)
    init_val :: Int -> a
    init_val = const 0

但它有很多错误。我对 haskell 的直觉是错误的。

【问题讨论】:

    标签: haskell fold


    【解决方案1】:

    你有

    safe_search :: [a] -> Int -> Maybe a
    safe_search xs n = foldr iteration init_val xs n
    

    如果null xs 成立,foldr iteration init_val [] => init_val,那么

    init_val n
    

    必须有意义。没有什么可退货的,所以

                 = Nothing
    

    这是我们在这里所能做的,以适应返回类型。

    所以init_val 是一个函数:: Int -> Maybe a。根据foldr的定义,这也是组合函数的“递归”参数,“来自右边”:

    iteration x r 
    

    但是这个调用本身也必须返回这样一个函数(同样,根据foldrfoldr f z [a,b,c,...,n] == f a (f b (f c (...(f n z)...)))f :: a -> b -> b 的定义,即它必须返回一个与第二个参数相同类型的值),所以

                   n | n==0 = Just x
    

    这很简单,第 0 个元素就在手边,x;如果n > 0怎么办?

                     | n>0  = ... (n-1)
    

    对吗?只剩下一步让你自己做...... :) 不是x(列表的元素)在那里的点上;它必须是一个函数。我们已经收到了这样的函数,作为参数......

    要查看这里发生了什么,首先检查输入是单元素列表时的情况可能会有所帮助,

    safe_search [x] n = foldr iteration init_val [x] n
                      = iteration x init_val n
    

    并且有两个元素,

                [x1, x2] n = iteration x1 (iteration x2 init_val) n
            --               iteration x  r                       n
    

    希望现在清楚了。

    编辑:因此,这类似于通常的基于foldrzip 实现与n 向下的降序枚举融合,确实编码了更高级别的定义

    foo xs n = ($ zip xs [n,n-1..]) $ 
                            dropWhile ((>0) . snd) >>>
                            map fst >>>
                            take 1 >>> listToMaybe
             = drop n >>> take 1 >>> listToMaybe $ xs
    

    【讨论】:

      【解决方案2】:

      想想一些事情。

      1. init_val 应该有什么类型?

      2. g 需要做什么? g 是这段代码中最棘手的部分。如果您曾经了解过延续传递风格,您可能应该将init_valg 都视为延续。

      3. x 代表什么?你需要用它做什么?

      我前段时间写了一篇explanation,关于foldl 的定义如何根据foldr 工作。您可能会发现它很有帮助。

      【讨论】:

        【解决方案3】:

        我建议使用标准的foldr模式,因为使用标准函数时更容易阅读和理解代码:

        1. foldr 的类型为 foldr :: (a -> b -> b) -> [a] -> b -> [b], 其中第三个参数 b 是列表元素 [a] 的累加器 acc
        2. 在添加了所需的列表元素后,您需要停止将列表中的元素 [a] 添加到 acc。然后你从结果列表[b] 中取出head,从而得到列表[a] 的所需元素。
        3. 要获得n'列表xs的第一个元素,您需要将xslength xs - n元素添加到累加器acc中,从列表末尾开始计数。
        4. 但是如果我们想使用标准的foldr 函数来提高代码的可读性,应该在哪里使用迭代器呢?我们可以在累加器中使用它,将其表示为元组(acc, iterator)。我们从iterator 中减去1 每一轮我们将初始列表xs 中的元素添加到acc 中,当我们的iterator 等于@987654346 时停止将xs 的元素添加到acc @。
        5. 然后我们将head . fst 应用于foldr 函数的结果,以获取初始列表xs 的所需元素,并用Just 构造函数包装它。
        6. 当然,如果我们初始列表xs中的length - 1小于所需元素的索引n,则整个函数safeSearch的结果将是Nothing

        这里是函数safeSearch的代码:

        safeSearch :: Int -> [a] -> Maybe a
        safeSearch n xs
          | (length xs - 1) < n = Nothing
          | otherwise           = return $ findElem n' xs
          where findElem num =
                  head .
                  fst .
                  foldr (\x (acc,iterator) ->
                           if iterator /= 0
                              then (x : acc,iterator - 1)
                              else (acc,iterator))
                        ([],num)
        
                n' = length xs - n
        

        【讨论】:

        • length 通常是个坏主意,这也不例外。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-12-24
        • 2021-08-29
        • 1970-01-01
        • 2014-01-11
        • 1970-01-01
        • 2021-08-29
        相关资源
        最近更新 更多