Чи реалізація цієї функції слів можлива без кроку після обробки після складання?


14

Реальний світ Haskell, глава 4, сторінка 98 друку запитує, чи wordsможна реалізувати за допомогою складок, і це також моє питання:

Це можливо? Якщо ні, то чому? Якщо так, то як?

Я придумав наступне, що ґрунтується на ідеї, що кожне не пробіл має бути попередньо до останнього слова у списку виводу (це відбувається у otherwiseзахисті), і пробіл повинен викликати додавання порожнього слова до список вихідних даних, якщо його вже немає (це обробляється у 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

Очевидно, що це рішення є неправильним, оскільки провідні пробіли у вхідному рядку призводять до однієї провідної порожньої рядка у списку вихідних рядків.

На посиланні вище я розглянув декілька запропонованих рішень для інших читачів, і багато з них працюють аналогічно моєму рішенню, але вони, як правило, "опрацьовують" висновок складки, наприклад, tailпроводячи його, якщо є - порожній провідний рядок.

Інші підходи використовують кортежі (насправді просто пари), так що складка має справу з парою і може добре обробляти провідні / кінцеві простори.

У всіх цих підходах foldr(або інша складка, fwiw) - це не функція, яка забезпечує кінцевий вихід із коробки; завжди є щось інше, що має якось коригувати вихід.

Тому я повертаюся до початкового питання і запитую, чи реально реалізувати words(таким чином, щоб він правильно обробляв проміжні / ведучі / повторні пробіли) за допомогою складок. З допомогою згинів я маю в виду , що функція складання повинна бути зовнішня функція:

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

Відповіді:


13

Якщо я правильно розумію, ваші вимоги включають

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

Це означає, що ми не можемо мати

words = foldr step base

для будь-якого stepі base.

Дійсно, якби ми мали це, значить

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"

і це суперечить (2).

Ви обов'язково потребуєте деякої післяобробки після foldr.


1
Я більше люблю цю мову ...
Енріко Марія Де Анджеліс

Або навіть ["xa"] == words "xa" == step 'x' (words "a") == step 'x' (words " a") == words "x a" == ["x", "a"], що має перевагу бути
вагомим

5

@chi має чудовий аргумент, що ви не можете реалізувати, wordsвикористовуючи складку "a", але ви сказали, використовуючи 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

І зовнішня, і внутрішня функція - це складки. ;-)


Я думаю, ви знаєте, що я мав на увазі, але +1 за те, що я був вибагливим: P
Енріко Марія Де Анджеліс,

1

Так. Навіть хоча це трохи хитро, ви все одно можете виконати цю роботу належним чином, використовуючи єдиний foldrі нічого іншого, якщо ви зупинитесь на CPS ( стиль продовження передачі ). Я раніше демонстрував особливий вид chunksOfфункції.

У таких видах складок наш акумулятор, отже, результат складання - це функція, і ми повинні застосувати його до вхідної ідентичності, щоб мати кінцевий результат. Отже, це може вважатися завершальним етапом обробки чи ні, оскільки ми тут використовуємо одну складку, а тип її включає функцію. Відкриті для дебатів :)

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 : Початкове значення функції для початку.

go : Функція ітератора

Ми фактично не повністю використовуємо потужність CPS тут, оскільки у нас є як попередній, так pcі точний символ cпід рукою на кожному кроці. Це було дуже корисно в chunksOfфункції згаданої вище в той час як відриви [Int]в [[Int]]кожен раз , коли висхідна послідовність елементів були розбиті.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.