L'implementazione di questa funzione di parole è possibile senza una fase di postelaborazione dopo la piegatura?


14

Real World Haskell, capitolo 4, pagina 98 della stampa chiede se wordspuò essere implementato usando le pieghe, e anche questa è la mia domanda:

È possibile? Se no, perché? Se lo è, come?

Ho trovato quanto segue, che si basa sull'idea che ogni non spazio dovrebbe essere anteposto all'ultima parola nell'elenco di output (ciò accade nella otherwiseprotezione) e che uno spazio dovrebbe innescare l'aggiunta di una parola vuota a l'elenco di output se non ce n'è già uno (questo è gestito in if- then- else).

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

Chiaramente questa soluzione è sbagliata, poiché gli spazi iniziali nella stringa di input generano una stringa vuota iniziale nell'elenco di stringhe di output.

Al link sopra, ho esaminato alcune delle soluzioni proposte per altri lettori e molte di loro funzionano in modo simile alla mia soluzione, ma generalmente "post-processano" l'output del fold, ad esempio tailinserendolo se è una stringa iniziale vuota.

Altri approcci usano tuple (in realtà solo coppie), in modo che la piega si occupi della coppia e possa gestire bene gli spazi iniziali / finali.

In tutti questi approcci, foldr(o un'altra piega, prima) non è la funzione che fornisce l'output finale fuori dalla scatola; c'è sempre qualcos'altro che deve regolare l'output in qualche modo.

Quindi torno alla domanda iniziale e chiedo se è effettivamente possibile implementare words(in modo che gestisca correttamente gli spazi finali / iniziali / ripetuti) usando le pieghe. Usando le pieghe intendo che la funzione di piegatura deve essere la funzione più esterna:

myWords :: String -> [String]
myWords input = foldr step seed input

Risposte:


13

Se capisco correttamente, i tuoi requisiti includono

(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"

Ciò implica che non possiamo avere

words = foldr step base

per qualsiasi stepe base.

Anzi, se lo avessimo, allora

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"

e questo contraddice (2).

Hai sicuramente bisogno di un po 'di post-elaborazione dopo il foldr.


1
Adoro questa lingua ancora di più ...
Enrico Maria De Angelis,

O addirittura ["xa"] == words "xa" == step 'x' (words "a") == step 'x' (words " a") == words "x a" == ["x", "a"], che ha il vantaggio di essere un valido argomento per entrambe le direzioni di piega
Cireo,

5

@chi ha un argomento meraviglioso che non puoi implementare wordsusando "a" fold, ma hai detto usando 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

Sia la funzione più esterna che quella più interna sono pieghe. ;-)


Penso che tu sappia cosa volevo dire, ma +1 per essere pignolo: P
Enrico Maria De Angelis,

1

Sì. Anche se è un po 'complicato, potresti ancora fare questo lavoro correttamente usando un singolo foldre nient'altro se ti soffermi nel CPS ( Continuation Passing Style ). In precedenza avevo mostrato un tipo speciale di chunksOffunzione.

In questo tipo di pieghe il nostro accumulatore, quindi il risultato della piega è una funzione e dobbiamo applicarlo a un tipo di input di identità in modo da avere il risultato finale. Quindi questo può essere considerato come una fase di elaborazione finale o meno poiché stiamo usando una piega qui e il tipo di essa include la funzione. Aperto al dibattito :)

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 : Il valore della funzione iniziale con cui iniziare.

go : La funzione iteratore

In realtà non stiamo sfruttando appieno la potenza del CPS qui poiché abbiamo sia il personaggio precedente pcche il personaggio currect ca portata di mano in ogni turno. Era molto utile in chunksOffunzione di cui sopra mentre chunking una [Int]in [[Int]]ogni volta una sequenza ascendente di elementi erano rotte.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.