Ist die Implementierung dieser Wortfunktion ohne einen Nachbearbeitungsschritt nach dem Falten möglich?

14

Real World Haskell, Kapitel 4, Seite 98 des Drucks fragt, ob wordsmit Falten implementiert werden kann, und dies ist auch meine Frage:

Ist es möglich? Wenn nicht, warum? Wenn ja, wie?

Ich kam auf Folgendes, das auf der Idee basiert, dass jedem Nicht-Leerzeichen das letzte Wort in der Ausgabeliste vorangestellt werden sollte (dies geschieht im otherwiseGuard) und dass ein Leerzeichen das Anhängen eines leeren Wortes an auslösen sollte die Ausgabeliste, falls noch keine vorhanden ist (dies wird im if- then- behandelt else).

myWords :: String -> [String]
myWords = foldr step [[]]
  where
    step x yss@(y:ys)
      | x == ' ' = if y == "" then yss else "":yss
      | otherwise = (x:y):ys

Diese Lösung ist eindeutig falsch, da führende Leerzeichen in der Eingabezeichenfolge zu einer führenden leeren Zeichenfolge in der Ausgabeliste der Zeichenfolgen führen.

Unter dem obigen Link habe ich einige der vorgeschlagenen Lösungen für andere Leser untersucht, und viele von ihnen funktionieren ähnlich wie meine Lösung, aber sie "verarbeiten" im Allgemeinen die Ausgabe der Falte nach, indem tailsie sie beispielsweise dort bereitstellen ist eine leere führende Zeichenfolge.

Andere Ansätze verwenden Tupel (eigentlich nur Paare), so dass die Falte sich mit dem Paar befasst und die führenden / nachfolgenden Leerzeichen gut handhaben kann.

Bei all diesen Ansätzen foldr(oder einer anderen Falte, fwiw) ist dies nicht die Funktion, die die endgültige Ausgabe sofort liefert. Es gibt immer etwas anderes, bei dem die Ausgabe irgendwie angepasst werden muss.

Daher gehe ich zurück zur ursprünglichen Frage und frage, ob es tatsächlich möglich ist, wordsmithilfe von Falten zu implementieren (so, dass nachfolgende / führende / wiederholte Leerzeichen korrekt behandelt werden). Mit Falten meine ich, dass die Faltfunktion die äußerste Funktion sein muss:

myWords :: String -> [String]
myWords input = foldr step seed input
Enrico Maria De Angelis
quelle

Antworten:

13

Wenn ich richtig verstehe, umfassen Ihre Anforderungen

(1) words "a b c" == words " a b c" == ["a", "b", "c"]
(2) words "xa b c" == ["xa", "b", "c"] /= ["x", "a", "b", "c"] == words "x a b c"

Dies impliziert, dass wir nicht haben können

words = foldr step base

für jeden stepund base.

In der Tat, wenn wir das hätten, dann

words "xa b c"
= def words and foldr
step 'x' (words "a b c")
= (1)
step 'x' (words " a b c")
= def words and foldr
words "x a b c"

und das widerspricht (2).

Sie brauchen definitiv eine Nachbearbeitung nach dem foldr.

Chi
quelle
1
Ich liebe diese Sprache mehr und mehr ...
Enrico Maria De Angelis
Oder sogar ["xa"] == words "xa" == step 'x' (words "a") == step 'x' (words " a") == words "x a" == ["x", "a"], was den Vorteil hat, ein gültiges Argument für jede Richtung der Falte zu sein
Cireo
5

@chi hat ein wunderbares Argument, dass Sie nicht wordsmit "a" Fold implementieren können , aber Sie haben gesagt, mit Fold s .

words = filterNull . words1
    where
    filterNull = foldr (\xs -> if null xs then id else (xs:)) []
    words1 = foldr (\c -> if c == ' ' then ([]:) else consHead c) []
    consHead c []       = [[c]]
    consHead c (xs:xss) = (c:xs):xss

Sowohl die äußerste als auch die innerste Funktion sind Falten. ;-);

luqui
quelle
Ich denke du weißt was ich meinte, aber +1 für wählerisch: P
Enrico Maria De Angelis
1

Ja. Obwohl es ein wenig schwierig ist, können Sie diesen Job dennoch ordnungsgemäß ausführen, indem Sie einen einzelnen foldrund nichts anderes verwenden, wenn Sie sich mit CPS ( Continuation Passing Style ) befassen . Ich hatte eine besondere Art von gezeigtchunksOf Funktion gezeigt.

Bei dieser Art von Falten ist unser Akkumulator, daher ist das Ergebnis der Faltung eine Funktion, und wir müssen es auf eine Identitätseingabe anwenden, damit wir das Endergebnis erhalten. Dies kann also als letzte Verarbeitungsstufe gelten oder nicht, da wir hier eine einzelne Falte verwenden und deren Typ die Funktion enthält. Offen für Debatten :)

ws :: String -> [String]
ws str = foldr go sf str $ ""
         where
         sf :: String -> [String]
         sf s = if s == " " then [""] else [s]
         go :: Char -> (String -> [String]) -> (String -> [String])
         go c f = \pc -> let (s:ss) = f [c]
                         in case pc of
                            ""        -> dropWhile (== "") (s:ss)
                            otherwise -> case (pc == " ", s == "") of
                                         (True, False)  -> "":s:ss
                                         (True, True)   -> s:ss
                                         otherwise      -> (pc++s):ss

λ> ws "   a  b    c   "
["a","b","c"]

sf : Der anfängliche Funktionswert, mit dem begonnen werden soll.

go : Die Iteratorfunktion

Wir nutzen die Leistung des CPS hier tatsächlich nicht vollständig aus, da wir in jeder Runde sowohl den vorherigen pcals auch den richtigen Charakter czur Hand haben. Es war sehr nützlich bei der chunksOfFunktion oben erwähnt , während eines Chunking [Int]in [[Int]]jede Zeit eine aufsteigende Folge von Elementen gebrochen.

Reduzieren
quelle