Написанную вами функцию нельзя сократить только с помощью оператора Prelude
, потому что невозможно сократить оператор if
. (Эрих указывает, что это можно сделать с помощью bool
из Data.Bool
.) Здесь я разрабатываю альтернативное лечение, которое можно сократить, но я надеюсь, что к концу я убедил вас не делать этого.
Здесь вам может пригодиться функция break
. Из Hackage:
примененный к предикату p и списку xs, возвращает кортеж, где первый элемент является самым длинным префиксом (возможно, пустым) xs элементов, которые не удовлетворяют p, а второй элемент является остатком списка
Таким образом, мы можем создать функцию для разделения вашего списка на определенный элемент:
splitOnChar :: Char -> String -> (String, String)
splitOnChar char = break (char ==)
Оттуда мы можем разработать функцию, как вы описываете:
replaceChar :: Char -> String -> String -> String
replaceChar char repstr instr =
case break (char ==) instr of
(front, _:back) -> front ++ repstr ++ back
_ -> error "Character to replace not found!"
Это позволяет вам избавиться от оператора if
, который невозможно написать без точек. Почему, черт возьми, вы хотите написать эту точку свободной, я не понимаю, но для этого нам нужно пожертвовать нашей обработкой ошибок. Давайте посмотрим на версию, которая отказывается от обработки
replaceChar :: Char -> String -> String -> String
replaceChar char repstr instr =
let ~(front, _:back) = break (char ==) instr
in front ++ repstr ++ back
Затем мы можем заменить front
и back
выражениями:
replaceChar :: Char -> String -> String -> String
replaceChar char repstr instr =
let split = break (char ==) instr
in (fst split) ++ repstr ++ (tail $ snd split)
Теперь давайте переместим split
в конец оператора in
:
replaceChar :: Char -> String -> String -> String
replaceChar char repstr instr =
let split = break (char ==) instr
in (\a b -> a ++ repstr ++ b) <$> fst <*> tail . snd $ split
Теперь мы можем заменить split
:
replaceChar :: Char -> String -> String -> String
replaceChar char repstr instr =
(\a b -> a ++ repstr ++ b) <$> fst <*> tail . snd $ break (char ==) instr
Далее давайте восстановим b
из нашего лямбда-выражения:
replaceChar :: Char -> String -> String -> String
replaceChar char repstr instr =
(\a -> ((a ++ repstr) ++)) <$> fst <*> tail . snd $ break (char ==) instr
Затем сделайте то же самое с a
:
replaceChar :: Char -> String -> String -> String
replaceChar char repstr instr =
((. (repstr ++)) . (++)) <$> fst <*> tail . snd $ break (char ==) instr
Затем замените instr
:
replaceChar :: Char -> String -> String -> String
replaceChar char repstr =
(((. (repstr ++)) . (++)) <$> fst <*> tail . snd) . break (char ==)
Далее проще уменьшить char
, поэтому мы просто поменяем местами аргументы и не забудем вернуть их flip
позже.
replaceChar :: String -> Char -> String -> String
replaceChar repstr char =
(((. (repstr ++)) . (++)) <$> fst <*> tail . snd) . break (char ==)
А теперь собственно уменьшим char
:
replaceChar :: String -> Char -> String -> String
replaceChar repstr =
((((. (repstr ++)) . (++)) <$> fst <*> tail . snd) .) . break . (==)
Теперь нам нужно переставить всю функцию, чтобы получить repstr
в конце. Начните с превращения . break . (==)
в раздел:
replaceChar :: String -> Char -> String -> String
replaceChar repstr =
(. break . (==)) $ ((((. (repstr ++)) . (++)) <$> fst <*> tail . snd) .)
Разделить вторую половину:
replaceChar :: String -> Char -> String -> String
replaceChar repstr =
(. break . (==)) $ (.) $ ((. (repstr ++)) . (++)) <$> fst <*> tail . snd
Раздел <*> tail . snd
replaceChar :: String -> Char -> String -> String
replaceChar repstr =
(. break . (==)) $ (.) $ (<*> tail . snd) $ ((. (repstr ++)) . (++)) <$> fst
Раздел <$> fst
:
replaceChar :: String -> Char -> String -> String
replaceChar repstr =
(. break . (==)) $ (.) $ (<*> tail . snd) $ (<$> fst) $ (. (repstr ++)) . (++)
Раздел . (++)
:
replaceChar :: String -> Char -> String -> String
replaceChar repstr =
(. break . (==)) $ (.) $ (<*> tail . snd) $ (<$> fst) $ (. (++)) $ (. (repstr ++))
Раздел (. (repstr ++))
:
replaceChar :: String -> Char -> String -> String
replaceChar repstr =
(. break . (==)) $ (.) $ (<*> tail . snd) $ (<$> fst) $ (. (++)) $ flip (.) $ (repstr ++)
Раздел (repstr ++)
:
replaceChar :: String -> Char -> String -> String
replaceChar repstr =
(. break . (==)) $ (.) $ (<*> tail . snd) $ (<$> fst) $ (. (++)) $ flip (.) $ (++) repstr
Эта-уменьшить:
replaceChar :: String -> Char -> String -> String
replaceChar =
(. break . (==)) . (.) . (<*> tail . snd) . (<$> fst) . (. (++)) . flip (.) . (++)
И flip
, чтобы вернуть аргументы в правильном порядке:
replaceChar :: Char -> String -> String -> String
replaceChar =
flip $ (. break . (==)) . (.) . (<*> tail . snd) . (<$> fst) . (. (++)) . flip (.) . (++)
Et-voilà: совершенно неразборчивая куча тарабарщины, которая волшебным образом делает то, что вам нужно, по непонятной для человечества причине.
person
Andrew Ray
schedule
23.04.2020
replace
. (Даже нечитаемый требует, чтобы вы импортировалиData.Bool
, чтобы получить доступ к функцииbool
, необходимой для замены выраженияif
.) - person chepner   schedule 23.04.2020replace '*' "foo" "foo*"
должно производить"foofoo"
, если я понимаю ваше описание, а не"foobar"
. Последнее невозможно, если только оно всегда не использует"bar"
в качестве значения замены. - person Andrew Ray   schedule 23.04.2020