Как перебрать список символов и манипулировать персонажами в Haskell? - PullRequest
0 голосов
/ 27 февраля 2019

Я пытаюсь просмотреть список символов в списке и что-то сделать с текущим персонажем.Мой Java-эквивалент того, что я пытаюсь сделать:

public class MyClass {
    void repeat(String s) {
        String newString = "";

        for(int i = 0; i < s.length(); i++) {
          newString += s.charAt(i);
          newString += s.charAt(i);
        }

 public static void main(String args[]) {
    MyClass test = new MyClass();
    test.repeat("abc");
  }
}

Ответы [ 2 ]

0 голосов
/ 27 февраля 2019

Одной из самых приятных вещей в функциональном программировании является то, что подобные вам шаблоны могут быть заключены в одну функцию более высокого порядка;если ничего не подходит, вы все равно можете использовать рекурсию.

Рекурсия

Сначала простое рекурсивное решение.Идея заключается в том, что это похоже на цикл for:

recursiveFunction [] = baseCase
recursiveFunction (char1:rest) = (doSomethingWith char1) : (recursiveFunction rest)

Итак, давайте напишем вашу repeat функцию в этой форме.Каков базовый случай?Хорошо, если вы repeat пустая строка, вы получите пустую строку обратно.Что такое рекурсия?В этом случае мы удваиваем первый символ, а затем повторяем по остальной части строки.Итак, вот рекурсивное решение:

repeat1 [] = []
repeat1 (c:cs) = c : c : (repeat1 cs)

Функции высшего порядка

Когда вы начнете писать больше на Haskell, вы обнаружите, что такого рода рекурсивные решения часто вписываются в несколько повторяющихся шаблонов.К счастью, стандартная библиотека содержит несколько предопределенных рекурсивных функций для шаблонов такого типа:

  • fmap используется для сопоставления каждого элемента списка с другим значением, используя функцию, заданную в качестве параметра.Например, fmap (\x -> x + 1) добавляет 1 к каждому элементу списка.К сожалению, он не может изменить длину списка, поэтому мы не можем использовать fmap.
  • concat используется для «выравнивания» вложенного списка.Например, concat [[1,2],[3,4,5]] - это [1,2,3,4,5].
  • foldr / foldl - это две более сложные и общие функции.Для получения более подробной информации обратитесь к Learn You a Haskell .

Ни один из них, похоже, не соответствует вашим потребностям.Однако мы можем использовать concat и fmap вместе:

repeat2 list = concat $ fmap (\x -> [x,x]) list

Идея состоит в том, что fmap заменяет, например, [1,2,3] на вложенный список [[1,1],[2,2],[3,3]], который затем выравнивается, а затем выравнивается.Такая схема генерации нескольких элементов из одного настолько распространена, что комбинация даже имеет специальное имя: concatMap.Вы используете это так:

repeat3 list = concatMap (\x -> [x,x]) list

Лично я вот как написал бы repeat на Хаскеле.(Ну, почти : я бы использовал eta-Reduction , чтобы упростить это немного больше. Но на вашем уровне это не имеет значения.) Именно поэтому Haskell, на мой взгляд, гораздо более мощныйпо сравнению со многими другими языками: этот 7-строчный Java-метод представляет собой одну строку хорошо читаемого идиоматического языка Haskell!

0 голосов
/ 27 февраля 2019

Как и другие предлагали, вероятно, разумно начать с понимания списка:

-- | Repeat each element of a list twice.
double :: [x] -> [x]
double xs = [d | x <- xs, d <- [x, x]]

Но тот факт, что второй список в понимании всегда имеет одинаковое количество элементов, независимо от значенияx означает, что нам не нужно так много энергии: достаточно интерфейса Applicative.Давайте начнем с написания понимания немного по-другому:

double xs = xs >>= \x -> [x, x] >>= \d -> pure d

Мы можем сразу упростить, используя закон тождества монад:

double xs = xs >>= \x -> [x, x]

Теперь мы переключимся на Applicative, но давайте оставимотверстие для жесткой части:

double :: [x] -> [x]
double xs = liftA2 _1 xs [False, True]

Компилятор сообщает нам, что

_1 :: x -> Bool -> x

Поскольку элементы внутреннего / второго списка всегда одинаковы и всегда происходят изтекущий внешний / первый элемент списка, нам не нужно заботиться о Bool:

double xs = liftA2 const xs [False, True]

Действительно, нам даже не нужно быть способным , чтобы различать списокпозиции:

double xs = liftA2 const xs [(),()]

Конечно, у нас есть специальный Applicative метод (<*), который соответствует liftA2 const, поэтому давайте использовать его:

double xs = xs <* [(),()]

И затемЕсли хотите, мы можем избежать упоминания xs, переключившись на форму «без точек»:

-- | Repeat each element of a list twice.
double :: [x] -> [x]
double = (<* [(),()])

Теперь для теста:

main :: IO ()
main = print $ double [1..3]

Это напечатает [1,1,2,2,3,3].


double допускает небольшое обобщение сомнительного значения:

double :: Alternative f => f x -> f x
double = (<* join (<|>) (pure ()))

Это будет работать и для последовательностей:s списки:

double (Data.Sequence.fromList [1..3]) = Data.Sequence.fromList [1,1,2,2,3,3]

, но это может быть немного запутанным для некоторых других Alternative экземпляров:

double (Just 3) = Just 3
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...