Итерация по строке и замена одиночных символов на подстроки в haskell

Я пытаюсь выучить немного Хаскелла, и мне трудно. У меня есть некоторые проблемы с моим текущим проектом. Идея заключается в том, что мне нужно пройти через строку и заменить некоторые символы новыми подстроками. Например, если у меня есть строкаFLXF» и я хочу заменить каждый F подстрокой под названием "ФНЧ» результат должен бытьFLFLXFLF», Сейчас я работаю над этой конкретной проблемой часами. Я читал о типах, различных функциях, которые могут пригодиться (карта, сгиб и т. Д.), И все же я не смог решить эту проблему.

Приведенный ниже код - это некоторые из моих попыток:

apply :: String -> String
apply []     = []
apply (x:xs) = if (x == 'F')
               then do show "Hello"
                       apply xs
               else (apply (xs))

Этот пример здесь я просто пытался показать привет каждый раз, когда я столкнулся с 'F', но все, что он показывает, это "", так что это явно не работает. Я действительно не уверен, что заявление if else - это путь сюда. Я также думал, что карта функций может помочь. Вот код, о котором я думал, может выглядеть примерно так:

map (\x y -> if y == 'F' then "FLD" else y) "FLF"

но это дает мне ошибку типа. Итак, как вы можете видеть, я потерян. Извините меня за плохое знание Хаскелла, но я все еще новичок в этом. Я действительно надеюсь, что некоторые из вас могут помочь мне здесь или дать мне толчок в правильном направлении. Не стесняйтесь задавать вопросы, если мне что-то неясно.

Заранее спасибо!

Джон

Ответы на вопрос(4)

что вы хотели заменить символ в строке на строку. Это невозможно, потому что String - это список Char, а Char - это Char, а не короткая строка. Строка также не является символом, даже если его длина равна 1.

Следовательно, то, что вы действительно хотели, это заменить некоторые символы на некоторые другие символы. Ваш подход был многообещающим и мог быть завершен так:

replace [] = []  -- nothing to replace in an empty string
replace (c:cs) = if c == 'F' then 'F':'L':'F':replace cs
                             else c:replace cs

если-то-иначе... Я знаю, что Haskell поддерживает это, однако яочень Удивило, что их здесь никто не убрал ...

Ниже приведены мои решения для разных случаев замены.

ЗаменаперсонажЗаменасловаЗамена черезфункция на каждое слово

кошка replace.hs $

import Data.List (isPrefixOf)

replaceC :: Char -> Char -> String -> String
replaceC _ _ [] = []
replaceC a b (x:xs)
  | x == a    = b:replaceC a b xs
  | otherwise = x:replaceC a b xs

replaceW :: String -> String -> String -> String
replaceW a b s = unwords . map replaceW' $ words s
  where replaceW' x | x == a    = b
                    | otherwise = x

replaceF :: (String -> String) -> String -> String
replaceF f = unwords . map f . words

string = "Hello world ^fg(blue)"

main = do
    print string
    print $ replaceC 'o' 'z' string
    print $ replaceW "world" "kitty" string
    print . replaceF f . replaceW "world" "kitty" $ replaceC 'H' 'Y' string
  where f s | "^" `isPrefixOf` s = '^':'^':drop 1 s
            | otherwise = s

runhaskell replace.hs

"Hello world ^fg(blue)"
"Hellz wzrld ^fg(blue)"
"Hello kitty ^fg(blue)"
"Yello kitty ^^fg(blue)"
Решение Вопроса
map (\x y -> if y == 'F' then "FLD" else y) "FLF"

Во-первых ... почему функция принимает два аргумента?

map (\y -> if y == 'F' then "FLD" else y) "FLF"

Оставшаяся ошибка типа заключается в том, чтоthen филиал даетString, ноelse филиал даетChar (каждая ветвь должна давать значение одного типа). Итак, мысделаюelse филиал датьString вместо этого (напомним, чтоString это синоним[Char]):

map (\y -> if y == 'F' then "FLD" else [y]) "FLF"

Теперь проблема в том, что это дает вам[String] значение вместоString, Итак, мыобъединю все эти строки вместе:

concat (map (\y -> if y == 'F' then "FLD" else [y]) "FLF")

Это сочетаниеconcat а такжеmap достаточно часто, что тамs стандартная функция, которая объединяет их.

concatMap (\y -> if y == 'F' then "FLD" else [y]) "FLF"
 John doe17 окт. 2012 г., 15:33
У меня была идея, что я был близок. Большое спасибо dave4420! Я дал вам репутацию для этого ответа!
 dubiousjim18 окт. 2012 г., 00:16
А такжеconcatMap это так часто, что тамстандартное обобщение этого:flip (>>=), :-) Шутки в сторону,>>= будет делать для дерева монаду так же, как и для монады списка: заменять элементы деревьями или списками, сохраняя базовую структуру.

concatMap это самая интуитивная вещь здесь. Такое сочетание сочетания между структурой данных и функцией, которая сама по себе возвращает тип структуры данных (в данном случае, списка) и объединением результатов в единую "в обтяжку" список действительноочень часто встречается в Хаскеле, да и не только для списков.

Я хотел бы объяснить, почему ваша первая попытка компилируется вообще, и что она на самом деле делает - потому что это'полностью отличается от того, что вы, вероятно, думаете!

apply (x:xs) = if (x == 'F')

эта строка все еще совершенно ясна: вы просто берете первый символ из строки и сравниваете его с'F', На бит "пешеход» вручную разобрать строку, но хорошо. Ну, имя, которое вы дали функции, не особенно велико, но яЯ буду придерживаться этого здесь.

               then do show "Hello"

теперь это интересно. Вы наверное думаетеdo начинается список точек "сначала сделай это, потом сделай это... как в простомПривет, мирпример программы. Но всегда помните: в Хаскелеобычно нет такой вещи какпорядок в котором вещи рассчитаны. Это происходит только вIO контекст. Но там'нет нетIO в вашем коде!?!

Не уверен, что тымы слышали о чемIO на самом деле, в любом случае здесь вы идете: этосMonad, Те "мифический Хаскель строит тебямы только читаем в книгах...

Действительно, хотя это может привести немного далеко здесь,этот вопрос охватывает все, что нужно знать о сMonad! Как так?

Вот'Другой (правильный!) способ определить вашу функцию.

apply' str = do
   x <- str
   if (x == 'F')
    then "FLF"
    else return x

Так что я'Я использую этот странныйdo синтаксис, и это 'не вIOи это выглядит совершенно иначе, чем выбуду писать вIO, но это работает. Как?

   x <- str

Вdo нотация,variable <- action всегда означает что-то вродевозьми одно значение из этой монадической штуковины и назови этоx, Что ты'мы, наверное, видели что-то вроде

response <- getLine

что значит "принять пользовательский ввод из реального мира (изIO монада!) так и назовиresponse, Вx <- str, Это'это строка, которая у нас есть, а неIO действие. Итак, мы берем символ из строки - легко и приятно!

На самом деле этоне совсем верно, хотя. "взять персонаж" это то, что вы делаете сapply (x:xs) = ..., который просто берет первый. По сравнению,x <- str на самом деле занимаетвсе возможные символы из строки, один за другим. Если ты'Привыкание к процедурным языкам может показаться очень несовместимым сresponse <- getLineно на самом деле этонеgetLine также состоит изкаждый возможный вклад что пользователь может дать, и программа должна действовать в соответствии с этим.

   if (x == 'F')

ничего неожиданного здесь, но

    then "FLF"

Ничего себе! Просто так? Позволять'первый взгляд на следующую строку

    else return x

хорошо, этовыглядит знакомо, но на самом деле этонет. На других языках это будет означатьмы'сделано с нашей функцией,x это результат ", Но это'Очевидно, не то, что здесь происходит, потому чтоx являетсяCharи "тип возврата " изapply' являетсяString, В Хаскелеreturn на самом деле имеет мало общего с возвращением значений из функции, вместо этого это означаетпоместить это значение в монадический контекст, который мыработает в, Если бы монада былаIO, это было бы то же самое:вернуть это значение в контексте реального мира (это не означает печатать значение или что-то, просто передать его). Но здесь наш контекст представляет собой строку, а точнее список (из символов, так чтоэтоString).

Хорошо, так что еслиx не является'F' мы помещаем это обратно в строку. Это звучит достаточно разумно, но как насчетthen "FLF"? Обратите внимание, что я также могу написать это так:

   if (x == 'F')
    then do
       x' <- "FLF"
       return x'
    else return x

а это значит, я беру всех персонажей из"FLW" и вернуть их обратно в общий результат. Но там'не нужно думать только об окончательном результате, мы можем также выделить только эту частьdo { x' <- "FLF"; return x' } - и, совершенно очевидно, его значение является ничем иным, как строкой"FLF" сам!

Надеюсь, вы поняли, почемуapply' работает. Вернуться к вашей версии, хотя на самом деле это не такне имеет большого смысла ...

           then do
              show "Hello"
              apply xs

здесь у нас есть строка, котораяне в концеdo блокировать, но нене иметь<- в этом. Вы обычно видите это вIO в чем-то вроде

main = do
   putStrLn "How ya doin'?"
   response <- getLine
   ...

Помни этоВыход только» действия имеют типIO() в Хаскеле, что означает, что они неt напрямую возвращает любое значимое значение, только тривиальное значение(), Так ты неэто действительно волнует, но вы все равно можете оценить это:

main = do
   trivial <- putStrLn "Hello, let's see what this IO action returns:"
   print trivial

компилирует и выводит

Привет, давайs посмотрим, что возвращает это действие IO:

()

Было бы глупо, если бы нам пришлось делать это, оценивая() все время, так что Haskell позволяет просто оставить() <- из. Это'Это действительно так!

Так что строка какshow "Hello" в серединеdo блок в основном означаетвзять одного персонажа изshow "Hello" (это просто строка со значением"\"Hello\""), но нес этим персонажем больше ничего не делать / просто выбросить ".

Остальная часть вашего определения - это просто другие рекурсивные вызовыapply, но поскольку ни один из них не делает ничего более интересного, чем выбрасывание персонажей, в конечном итоге выapply [] = [], чтобы's окончательный результат: пустая строка.

Ваш ответ на вопрос