2017-05-06 4 views
0

Hier ist ein Beispiel, was ich machen wollte.Was ist falsch an diesem Haskell-Listencode?

let b = ["this","is","a","test!"] 

"xx" ++ (b!!3) 

Das wird mir "xxtest!"

Wenn die Liste eine beliebige Zeichenfolge mit einem Ausrufezeichen enthält, wird "xx" zu dieser spezifischen Zeichenfolge hinzugefügt. Meine Frage ist, wie dies in eine korrekte Funktion umgesetzt werden kann.

Zur Zeit habe ich diese

replaceElement [] = [] 
replaceElement (x:xs) = 
     if '!' `elem` x 
     then ["xx"] ++ x : replaceElement xs 
     else x: replaceElement xs 

Aber diese Funktion nur „xx“ in der Liste als ein Element hinzufügen, wird es nicht in der Liste die spezifischen Zeichenfolge hinzugefügt werden. Wie kann ich "xx" ++ (b !! x) verwenden, wobei x die Position der Zeichenfolge mit einem Ausrufezeichen ist.

Antwort

4

Der Ausdruck

["xx"] ++ x : replaceElement xs 

als

["xx"] ++ (x : replaceElement xs) 

tatsächlich analysiert, die genau das tut, was Sie beschrieben: Einsätze "xx" in der Ergebnisliste. Was Sie wollen, anstatt zu tun, ist:

("xx" ++ x) : replaceElement xs 
3

Entscheidend ist, wie ["xx"] ++ x : replaceElement xs analysiert wird. Dies wird durch die fixities bestimmt des Betreibers:

GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help 
Prelude> :info : 
-- ... 
infixr 5 : 
Prelude> :i ++ 
(++) :: [a] -> [a] -> [a] -- Defined in ‘GHC.Base’ 
infixr 5 ++ 

Also, beide : und ++ sind rechtsassoziativ Operatoren mit gleicher Priorität. Rechtsassoziativ bedeutet, a : b : c wird geparst als a : (b : c) anstelle von (a : b) : c (wie es bei linksassoziativem infixl der Fall wäre). Aufgrund der gleichen Priorität gilt dies immer noch, wenn Sie : und ++, i.s.

["xx"] ++ x : replaceElement xs ≡ ["xx"] ++ (x : replaceElement xs) 

IOW, die Sie voranstellen nur ["xx"] auf das gesamte Ergebnis, aber die einzelnen Elemente nie mit "xx" in Kontakt treten. Um dies zu erreichen, müssen Sie die Gruppe 10 mit x gruppieren. Die zusätzlichen Klammern sind dann überflüssig (in der Tat könnten Sie sich davon abhalten lassen: das Umhüllen von "xs" in eine zusätzliche Ebene bedeutet, dass Sie nicht mehr wie vorgesehen auf der Zeichenfolgenebene arbeiten, sondern auf der Liste der Zeichenfolgenebenen).


Eine bessere Alternative wäre natürlich zu keiner manuellen Rekursion tun: Sie Anwendung einfach die gleiche Operation auf alle Elemente einer Liste; das ist, was map gibt es für:

replaceElement = map $ \x -> if '!'`elem`x 
           then "xx"++x 
           else x 
0

Sie auch wie map mit einer Hilfsfunktion verwenden können;

addxx :: [String] -> [String] 
addxx = map checkBang 
     where checkBang s | last s == '!' = "xx" ++ s 
          | otherwise  = s