J'ai une liste d'éléments et je souhaite les mettre à jour:
à partir de ceci: ["Off","Off","Off","Off"]
pour ça: ["Off","Off","On","Off"]
Comme je suis un peu nouveau pour Haskell, j'utilise (x:xs)!!y
pour extraire et mettre à jour des composants individuels à l'aide de la fonction:
replace y z [] = []
replace y z (x:xs)
| x==y = z:replace y z xs
| otherwise = x:replace y z xs
puis en saisissant ce qui suit dans ghci: (replace "Off" "On" ["Off",'Off","Off","Off"]) !! 2
J'obtiens ce qui suit: "On"
Il me semble pouvoir extraire et convertir des éléments d'une liste mais je n'arrive pas à obtenir une liste avec l'élément unique converti.
Toute aide à ce sujet serait appréciée.
Je ne sais pas trop ce que vous essayez de faire. Si vous avez seulement besoin de générer ["Off", "Off", "On", "Off"], vous pouvez le faire explicitement. D'une manière générale, il faut éviter de modifier l'état dans haskell.
Peut-être que vous voulez une fonction pour "modifier" (générer un nouvel élément avec une valeur différente) le nième élément d'une liste? Don donne une approche très générale de ce type de problème. Vous pouvez également utiliser la récursivité explicite:
replaceNth :: Int -> a -> [a] -> [a]
replaceNth _ _ [] = []
replaceNth n newVal (x:xs)
| n == 0 = newVal:xs
| otherwise = x:replaceNth (n-1) newVal xs
Haskell fournit d'excellentes fonctionnalités pour la manipulation de listes. Si vous ne les connaissez pas déjà, filter
, map
et foldr
/foldl
valent la peine d'être examinés, tout comme les listes de compréhension.
En règle générale, vous modifiez des éléments d'une liste en fractionnant la liste, en remplaçant un élément et en le réunissant à nouveau.
Pour diviser une liste en index, nous avons:
splitAt :: Int -> [a] -> ([a], [a])
que vous pouvez utiliser pour diviser une liste, comme ceci:
> splitAt 2 ["Off","Off","Off","Off"]
(["Off","Off"],["Off","Off"])
il suffit maintenant de faire apparaître l'élément head du composant snd
de la liste. Cela se fait facilement avec correspondance de motifs:
> let (x,_:ys) = splitAt 2 ["Off","Off","Off","Off"]
> x
["Off","Off"]
> ys
["Off"]
vous pouvez maintenant rejoindre la liste ensemble, avec un "On":
> x ++ "On" : ys
["Off","Off","On","Off"]
Je vous laisse le soin de rassembler ces pièces en une seule fonction.
En tant que note de style, je suggère d'utiliser un nouveau type de données personnalisé, au lieu de String
pour vos bascules:
data Toggle = On | Off deriving Show
Une opération courante dans de nombreuses langues consiste à affecter une position indexée dans un tableau. Dans python vous pourriez:
>>> a = [1,2,3,4,5]
>>> a[3] = 9
>>> a
[1, 2, 3, 9, 5]
Le package lens donne cette fonctionnalité avec le (.~)
opérateur. Bien que contrairement à python la liste d'origine n'est pas modifiée, une nouvelle liste est plutôt renvoyée.
> 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
n'est qu'une fonction et le (&)
L'opérateur, qui fait partie du package lens , n'est qu'une application de fonction inverse. Le voici avec une application de fonction plus courante.
> (element 3 .~ 9) [1,2,3,4,5]
[1,2,3,9,5]
L'affectation fonctionne à nouveau parfaitement avec l'imbrication arbitraire de Traversable
s.
> [[1,2,3],[4,5,6]] & element 0 . element 1 .~ 9
[[1,9,3],[4,5,6]]
ou
> set (element 3) 9 [1,2,3,4,5,6,7]
Ou si vous souhaitez effectuer plusieurs éléments, vous pouvez utiliser:
> over (elements (>3)) (const 99) [1,2,3,4,5,6,7]
> [1,2,3,4,99,99,99]
Ce n'est pas seulement limité aux listes, mais cela fonctionnera avec tout type de données qui est une instance de la classe de types Traversable .
Prenons par exemple la même technique qui fonctionne sur arbres forme le package standard containers .
> 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
Voici une doublure qui fonctionne parfaitement
replace pos newVal list = take pos list ++ newVal : drop (pos+1) list
Je ne semble pas efficace pour faire ce genre de choses dans haskell.
Voici du code que j'utilise:
-- | 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
En fait, dans de nombreux cas (pas toujours) où vous utiliseriez une liste, un Data.Vector est un meilleur choix.
Il est livré avec une fonction de mise à jour, voir Hackage , qui fait exactement ce dont vous avez besoin.
Je pense que vous devriez envisager d'utiliser une structure de données autre que List. Par exemple, si vous voulez simplement avoir un état de quatre interrupteurs marche/arrêt, alors:
data State = St { sw1, sw2, sw3, sw4 :: Bool }
Pour un nombre dynamique de commutateurs, envisagez un mappage à partir de switch name
à Bool
.
Je pense que c'est une manière plus élégante de remplacer un élément individuel:
setelt:: Int -> [a] -> a -> [a]
setelt i list newValue =
let (ys,zs) = splitAt i-1 list in ys ++ newValue ++ tail zs
Il existe une gestion des erreurs d'entrée. Donc, si l'index i est hors des limites, haskell affichera une sortie incorrecte. (Remarque: dans Haskell, l'indexation commence à partir de 1)
Les câlins se comporteraient comme suit:
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 []
Traitement des erreurs à votre service:
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
si l'index que vous donnez est incorrect, il renvoie la liste d'origine.
Cette réponse arrive assez tard, mais je pensais partager ce que je pense être une manière efficace de remplacer l'élément nth
dans une liste dans Haskell. Je suis nouveau chez Haskell et je pensais que j'allais intervenir.
La fonction set
définit le nième élément d'une liste à une valeur donnée:
set' :: Int -> Int -> a -> [a] -> [a]
set' _ _ _ [] = []
set' ind curr new arr@(x:xs)
| curr > ind = arr
| ind == curr = new:(set' ind (curr+1) new xs)
| otherwise = x:(set' ind (curr+1) new xs)
set :: Int -> a -> [a] -> [a]
set ind new arr = (set' ind 0 new arr)
Comme set
parcourt une liste, il casse la liste et si l'index actuel est le n
il combine l'élément précédent avec la nouvelle valeur donnée, sinon, il combine l'élément précédent avec le ancienne valeur dans la liste de cet index.