Рекурсивное обращение к строке (или списку)

#haskell

#haskell

Вопрос:

Я пытаюсь написать функцию в haskell, которая рекурсивно меняет списки. Я написал вспомогательную функцию, которая принимает исходный список и пустой список, а затем переносит элементы из первого в другой в шаблоне LIFO.

Вот что у меня есть:

 myreverse :: [a] -> [a]
myreverse list = myflip list []

myflip :: [a] -> [a] -> [a]
myflip list1 newList
    | null list1        = newList
    | otherwise         = myflip (tail list1) ((head list1) : newList)
  

Я знаю, что есть встроенная функция, которая делает это за меня, но требование состоит в том, чтобы я использовал только head, tail, elem и null (также без сопоставления с шаблоном). Итак, мой вопрос: есть ли лучшее решение, в котором у меня есть только одна функция, myreverse , которая использует только один список? (Конечно, это соответствует вышеуказанным требованиям)

Спасибо!

Комментарии:

1. Да. И то, что у меня есть, даст мне полную оценку. Я просто хочу знать, есть ли более элегантный способ сделать это, учитывая вышеуказанные требования.

2. Вы также используете (:) , но его нет в списке разрешенных функций 😉

3. Да, есть много способов сделать это, но ограничения затрудняют создание хорошей функции. 🙂

4. Это неразрешимо, потому что ни один из tail , elem, head и null не приводит к списку того же типа и, по крайней мере, такой же длины, как один из аргументов. Но результатом должен быть список того же типа и той же длины, что и аргумент. Q.E.D.

5. @sth (:) — это конструктор данных, а не конструктор типов. Его тип равен (:) :: a -> [a] -> [a] , поэтому он логически является функцией из-за его типа, содержащего хотя бы одну стрелку. Страница Википедии, посвященная алгебраическим типам данных , описывает конструктор данных как нечто похожее на функцию, но затем в следующем предложении говорится, что это логически функция. Конечно, если мы хотим иметь хоть какую-то надежду на решение проблемы, мы должны разрешить ее использование!

Ответ №1:

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

 reverse' :: [a] -> [a]  
reverse' = foldl (acc x -> x : acc) [] 
  

Комментарии:

1. Или немного более причудливый foldl (flip (:)) []

2. Спасибо, но это не соответствует требованиям. :

3. @Sadiq reverse' sNew = foldl (flip (:)) sNew принимает 2 списка и возвращает перевернутую строку. Имейте в виду, что Haskell всегда возвращает новое значение. Я не знаю, зачем вам когда-либо нужно было передавать значение для хранения вашего нового результата. Неизменяемые языки не подвержены тем же опасностям, что и императивные.

Ответ №2:

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

 myreverse :: [a] -> [a]
myreverse list = myflip list []
    where myflip list newList = if null list then newList
                                else myflip (tail list) ((head list):newList)
  

Спасибо всем за ваши комментарии и предложения.

Ответ №3:

сохраните это в файле reverse.hs

  reverseString :: [Char] -> [Char]
 reverseString [] = []
 reverseString (x:xs) = reverseString xs    [x]
  

затем запустите
reverseString «abc»

cba

Ответ №4:

Помимо настроек, необходимых для удовлетворения ваших дополнительных требований, ваша функция эквивалентна способу reverse реализации в GHC. Поэтому я бы предположил, что ваша функция довольно хороша такой, какая она есть.

Ответ №5:

Эта функция удовлетворяет вашим требованиям, но ужасно неэффективна:

 rev xs = if null xs then xs else rev (tail xs)    [head xs]
  

Ваше решение совсем не плохое. После использования сопоставления с образцом и создания myFlip локальной функции это не будет выглядеть уродливо. И техника использования локальной функции с аккумулятором очень распространена (хотя и не в таких простых случаях).

Комментарии:

1. Я думал об этом, но, к сожалению, оператор не соответствует требованиям. 🙁 Хотя спасибо за помощь!

2. @Uqi: тогда напишите свой собственный : app xs ys = if null xs then ys else head xs : app (tail xs) ys