#list #haskell #substring
#Список #haskell #подстрока
Вопрос:
Я должен разделить строку и вернуть подстроку, которая встречается перед переданным символом, но мы только начинаем Haskell, и для меня это похоже на китайский. Я возился с этим, но безуспешно. Вот что у меня есть до сих пор:
--spanString returns substring of string s before char c
spanString (c, [s])::(c, [s]) -> []
spanString (c, a:b) =
let (x, y) = spanString (c, b)
in
if a < c then (a:x,y)
else (x, a:y)
Что я напутал?
Ответ №1:
Прежде всего, ваша подпись типа полностью перепутана. Он должен либо отсутствовать, либо иметь вид spanString :: <some type>
. Даже если мы проигнорируем (c, [s])
положение перед двойным двоеточием, остальное все равно выглядит как-то странно. Его можно прочитать как «функцию, преобразующую значения типа (c, [s])
в значения типа []
для любых c и s» (c и s являются переменными типа). Во-первых, []
в Haskell нет типа. Не будет типа списка без его типа элемента. Далее, мы не можем работать ни с одним c
и s
. Мы должны иметь возможность сравнивать их, верно?
На самом деле, давайте пока не будем использовать полиморфизм и точно определим, какие типы мы хотим. Нам нужен символ и список символов, по какой-то причине упакованный в кортеж : (Char, [Char])
. Обратите внимание, что Char
начинается с заглавной буквы, что означает, что это не переменная типа, а конкретный тип. Как насчет нашего типа результата? Если вы доверяете описанию проблемы, вам нужно вернуть список символов ( [Char]
) , но если вы посмотрите на код, он, очевидно, возвращает кортежи списков ( ([Char], [Char])
) . Хорошо, может быть, второй список полезен, давайте пока оставим его:
spanString :: (Char, [Char]) -> ([Char], [Char])`
Теперь ваш код компилируется.
Однако при запуске происходит сбой с исключением: Non-exhaustive patterns in function spanString
. Это потому, что вы не обрабатываете случай, когда переданный список пуст. Если вы это сделаете, добавив уравнение типа
spanString (_, []) = ([], [])
ваша функция работает хорошо, но теперь давайте посмотрим, что она делает. Оказывается, у вас есть функция для разделения списка: она возвращает все символы заданной строки меньше, чем c
в качестве первого элемента кортежа, а все остальные символы — в качестве второго элемента. Мне кажется, что это ошибка (вы реализовали совершенно другую функцию!).
Комментарии:
1. Да, второй возвращенный список был моей ошибкой. Я вроде как терял надежду и был готов попробовать что угодно, когда впервые написал это, но определенно должен быть возвращен только один список. Похоже, я делаю это совершенно неправильно и не знаю, куда идти дальше.
Ответ №2:
Ошибка, довольно много.
Во-первых, ваше объявление типа неверно. Haskell использует имена типов в верхнем регистре и не передает параметры в скобках, как это делают большинство языков. Мы пишем
y = sin x
вместо
y = sin (x)
Вы, вероятно, хотите что-то вроде
spanString :: Char -> String -> String
Ваше определение spanString синтаксически верно, но все же неверно. Подумайте об этом так: если первый символ не совпадает, то вы хотите использовать spanString для остальной части строки, а затем вернуть результат с добавлением первого символа. Если первый символ действительно совпадает, то вы хотите вернуть «».
Комментарии:
1. Даже если тип аргумента является кортежем? Потому что я должен передать ему кортеж с символом и списком.
2. @Mike: в таком случае все в порядке; тогда у вас есть:
spanString :: (Char,String) -> String
, хотя это довольно не по-Хаскелли.
Ответ №3:
Ваше определение типа неверно .
spanString :: Char-> String-> String
spanString _ [] = []
spanString c (x:xs) | c==x = []
| otherwise = x:spanString c xs
Комментарии:
1. Ваш ответ правильный, но следует отметить, что вопрос был помечен как домашнее задание, а для домашнего задания обычно, как объясняется в описании тега, «направлять учащегося в решении проблемы, а не просто показывать полный ответ».
2. Это не полностью решает проблему, с которой я столкнулся, но это помогло. Принятый ответ и комментарии к нему — это то, что действительно помогло мне его получить.
Ответ №4:
Просто для информации, служебные функции, подобные этой, почти всегда можно найти в Prelude или в одной из стандартных библиотек. В этом случае takeWhile
поможет:
spanString :: (Char, String) -> String
spanString (c, s) = takeWhile (/= c) s
(т. е. продолжайте принимать символы, пока они не равны c
).
Передача аргументов в кортеже немного странная, но если это то, что требуется, пусть будет так.