簡體   English   中英

Haskell - 使用 foldl 或 foldr 而不是模式匹配來更新具有給定索引處的新值的列表

[英]Haskell - using foldl or foldr instead of pattern matching for updating a list with a new value at a given index

我已經實現了一個 function (,.=),它給出了一個列表和一個包含列表中索引和新值的元組,用給定索引處的新值更新給定列表。

(!!=) :: [a] -> (Int,a) -> [a]
(!!=) xs (0, a) = a : tail xs
(!!=) [] (i, a) = error "Index not in the list"
(!!=) (x:xs) (i, a) = x : xs !!= (i-1, a)

作為一個有折疊概念的初學者,我想知道是否有一種方法可以使用 foldl 或 foldr 來實現相同的結果?

提前非常感謝。

我會給你我認為更容易理解的foldl版本,以及我能想到的最簡單/最直接的版本。

但請注意,您不應該使用foldl (使用foldl' : https://wiki.haskell.org/Foldr_Foldl_Foldl' ) - 也不應該像這樣使用++ (使用:並在之后反轉);)

無論如何,這是一個想法:

(!!=) xs (i, a) = snd $ foldl 
   (\(j, ys) x -> (j+1, if j == i then ys ++ [a] else ys ++ [x])) 
   (0, []) 
   xs
  • 作為折疊的狀態/累加器,我采用當前索引和累加結果列表的元組(因此是snd ,因為我最后只想要這個)
  • 然后折疊 function 只需要查看我們是否在索引處並交換元素 - 返回下一個索引和新的累積列表

作為練習,您可以嘗試:

  • 使用:而不是++reverse
  • 重寫為foldr
  • 查看zipWith並使用此( zipWith (...) [0..] xs )而不是折疊(這類似於使用帶有索引的map

foldlfoldr都不能有效地完成這項特定的工作(除非您在折疊列表時通過模式匹配來“欺騙”它),盡管foldr可以做得不那么糟糕。 不,您真正需要的是一種不同的折疊方式,有時稱為para

para :: (a -> [a] -> b -> b) -> b -> [a] -> b
para _f n [] = n
para f n (a : as) = f a as (para f n as)

parafoldr非常相似。 它們中的每一個都采用組合 function 並且對於每個元素,傳遞該元素的組合 function 以及折疊列表的 rest 的結果。 但是para增加了一些額外的東西:它還通過了列表的 rest。 因此,一旦您到達替換點,就無需重建列表的尾部。

但是......你如何從一開始就計算foldrpara 這帶來了一個經典的技巧,有時稱為“高階棄牌”。 而不是para go stop xs生成一個list ,它將生成一個function ,它將插入 position 作為參數。

(!!=) :: [a] -> (Int, a) -> [a]
xs0 !!= (i0, new) = para go stop xs0 i0
  where
    -- If the list is empty, then no matter what index
    -- you seek, it's not there.
    stop = \_ -> error "Index not in the list"

    -- We produce a function that takes an index. If the
    -- index is 0, we combine the new element with "the rest of the list".
    -- Otherwise we apply the function we get from folding up the rest of
    -- the list to the predecessor of the index, and tack on the current
    -- element.
    go x xs r = \i -> case i of
      0 -> new : xs
      _ -> x : r (i - 1)

請注意, para很容易實現foldr

foldr c = para (\a _ b -> c a b)

可能不太明顯的是foldr可以實現para (非常低效的版本):

para f n = snd . foldr go ([], n)
  where
    go x ~(xs, r) = (x : xs, f x xs r)

以免您產生錯誤的想法並認為parafoldr “更好”,要知道當不需要它的額外功能時, foldr更易於使用,並且通常會被編譯為更高效的代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM