Подстрока в Haskell

#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 ).

Передача аргументов в кортеже немного странная, но если это то, что требуется, пусть будет так.