替换Haskell中的各个列表元素?

问题描述 投票:24回答:9

我有一个元素列表,我希望更新它们:

来自:["Off","Off","Off","Off"]

对此:["Off","Off","On","Off"]

由于我对Haskell有点新,我一直使用(x:xs)!!y使用函数提取和更新单个组件:

replace y z [] = []
replace y z (x:xs)
  | x==y           = z:replace y z xs
  | otherwise      = x:replace y z xs

然后在ghci中输入以下内容:(replace "Off" "On" ["Off",'Off","Off","Off"]) !! 2

我得到以下内容:"On"

我似乎能够提取和转换列表的元素,但我似乎无法获得转换单个元素的列表。

任何有关此事的帮助将不胜感激。

list haskell state
9个回答
15
投票

我不确定你要做什么。如果您只需要生成[“关闭”,“关闭”,“开启”,“关闭”],您可以明确地执行此操作。一般来说,应该避免在haskell中修改状态。

也许你想要的是一个“修改”(生成一个具有不同值的新元素)列表的第n个元素的函数?唐对这类问题给出了非常一般的解决方法。您还可以使用显式递归:

 replaceNth :: Int -> a -> [a] -> [a]
 replaceNth _ _ [] = []
 replaceNth n newVal (x:xs)
   | n == 0 = newVal:xs
   | otherwise = x:replaceNth (n-1) newVal xs

Haskell为列表操作提供了出色的功能。如果你不了解它们,qazxsw poi,qazxsw poi和qazxsw poi / filter都值得关注,列表理解也是如此。


47
投票

通常,您可以通过拆分列表,替换元素并将其重新连接在一起来修改列表的元素。

要在索引处拆分列表,我们有:

map

您可以使用它来分解列表,如下所示:

foldr

现在你只需要弹出列表的foldl组件的head元素。这可以通过模式匹配轻松完成:

 splitAt :: Int -> [a] -> ([a], [a]) 

您现在可以一起加入列表,并带有“开”:

 > splitAt 2 ["Off","Off","Off","Off"] 
 (["Off","Off"],["Off","Off"])

我会留给你把这些碎片组合成一个单独的功能。


作为样式注释,我建议使用新的自定义数据类型,而不是snd用于切换:

 > let (x,_:ys) = splitAt 2 ["Off","Off","Off","Off"]
 > x
 ["Off","Off"]
 > ys
 ["Off"]

27
投票

更改第n个元素

许多语言中的常见操作是分配给数组中的索引位置。在python中你可能会:

 > x ++ "On" : ys
 ["Off","Off","On","Off"]

String包使用 data Toggle = On | Off deriving Show 运算符提供此功能。虽然与python不同,原始列表不会发生变异,而是返回一个新列表。

>>> a = [1,2,3,4,5]
>>> a[3] = 9
>>> a
[1, 2, 3, 9, 5]

lens只是一个函数,(.~)运算符,> let a = [1,2,3,4,5] > a & element 3 .~ 9 [1,2,3,9,5] > a [1,2,3,4,5] 包的一部分,只是反向函数应用程序。这里有更常见的功能应用。

element 3 .~ 9

任务再次与(&)s的任意嵌套完美配合。

lens

要么

> (element 3 .~ 9) [1,2,3,4,5]
[1,2,3,9,5]

或者,如果您想要影响多个元素,您可以使用:

Traversable

使用其他类型然后列表

这不仅限于列表,但它适用于作为> [[1,2,3],[4,5,6]] & element 0 . element 1 .~ 9 [[1,9,3],[4,5,6]] 类型类的实例的任何数据类型。

举例来说,相同的技术适用于> set (element 3) 9 [1,2,3,4,5,6,7] ,形成标准的> over (elements (>3)) (const 99) [1,2,3,4,5,6,7] > [1,2,3,4,99,99,99] 包。

Traversable

5
投票

这是我一直在使用的一些代码:

trees

5
投票

这是一个完美运作的单线

containers

我似乎没有效率在haskell做这种事情。


2
投票

实际上,对于许多使用List的情况(并非总是如此),Data.Vector是更好的选择。

它带有更新功能,请参阅 > import Data.Tree > :{ let tree = Node 1 [ Node 2 [Node 4[], Node 5 []] , Node 3 [Node 6 [], Node 7 []] ] :} > putStrLn . drawTree . fmap show $ tree 1 | +- 2 | | | +- 4 | | | `- 5 | `- 3 | +- 6 | `- 7 > putStrLn . drawTree . fmap show $ tree & element 1 .~ 99 1 | +- 99 | | | +- 4 | | | `- 5 | `- 3 | +- 6 | `- 7 > putStrLn . drawTree . fmap show $ tree & element 3 .~ 99 1 | +- 2 | | | +- 4 | | | `- 99 | `- 3 | +- 6 | `- 7 > putStrLn . drawTree . fmap show $ over (elements (>3)) (const 99) tree 1 | +- 2 | | | +- 4 | | | `- 5 | `- 99 | +- 99 | `- 99 ,它可以完全满足您的需求。


1
投票

我认为你应该考虑使用List以外的数据结构。例如,如果您只想拥有四个开/关开关状态,那么:

-- | Replaces an element in a list with a new element, if that element exists.
safeReplaceElement
  -- | The list
  :: [a]
  -- | Index of the element to replace.
  -> Int
  -- | The new element.
  -> a
  -- | The updated list.
  -> [a]
safeReplaceElement xs i x =
  if i >= 0 && i < length xs
    then replaceElement xs i x
    else xs


-- | Replaces an element in a list with a new element.
replaceElement
  -- | The list
  :: [a]
  -- | Index of the element to replace.
  -> Int
  -- | The new element.
  -> a
  -- | The updated list.
  -> [a]
replaceElement xs i x = fore ++ (x : aft)
  where fore = take i xs
        aft = drop (i+1) xs

对于动态数量的开关,请考虑从replace pos newVal list = take pos list ++ newVal : drop (pos+1) list Hackage的映射。


0
投票

我相信这是更换单个元素的更优雅方式:

data State = St { sw1, sw2, sw3, sw4 :: Bool }

有输入错误处理。因此,如果索引i超出边界,则haskell将显示错误的输出。 (注意:在Haskell索引中从1开始)

拥抱会表现如下:

switch name

您服务时的错误处理:

Bool

如果您给出的索引是错误的,则返回原始列表。


0
投票

这个答案很晚才到,但我想我会分享我认为在Haskell中替换列表中的setelt:: Int -> [a] -> a -> [a] setelt i list newValue = let (ys,zs) = splitAt i-1 list in ys ++ newValue ++ tail zs 元素的有效方法。我是Haskell的新手并且认为我会投入。

Main> setelt 1 [1,2,3] 9 [9,2,3] Main> setelt 3 [1,2,3] 9 [1,2,9] Main> setelt 0 [1,2,3] 9 [9,2,3] Main> setelt 4 [1,2,3] 9 [1,2,3,9] Program error: pattern match failure: tail [] 函数将列表中的第n个元素设置为给定值:

setelt i y newValue = 
    if and [i>0, i<= length y]
    then let (ys,zs) = splitAt (i-1) y in  ys ++ [newValue] ++ tail zs
    else y

nth遍历列表时,它会将列表分开,如果当前索引是给定的set,它将前一个元素与给定的新值组合,否则,它将前一个元素与该索引的列表中的旧值组合在一起。

© www.soinside.com 2019 - 2024. All rights reserved.