2014-02-04 3 views
3

сегодня я много читал о Haskell, но эта формация сводит меня с ума. Я хочу как можно скорее понять основные ошибки, чтобы нормально начать кодирование. Функция здесь должна возвращать строку, которая начинается со следующей строки «math Sign», строка (2sdwds+asd)+3 должна возвращать +asd)+3. Вот кодСинтаксис Haskell, ошибки синтаксического анализа для манекенов

getToNextSign :: String -> String 
getToNextSign str = do 

let mathSigns = ['+' , '-' , '*' , '/' , '^' , ')'] 
let a = head str 
if a `elem` mathSigns 
then str 
else if tail str /= [] 
     then getToNextSign $ tail str 
     else [] 

main = do 
putStrLn $ getToNextSign "(2sdwds+asd)+3" 

Это дает мне «ошибка разбора на входе =». Я также не уверен, как именно назвать это в основном, и мне действительно нужна функция putStrLn. Я не думаю, что мне это нужно, но я пробовал как 2874 разных способов написать это, и теперь я просто сдался и нуждался в помощи.

+1

Является ли код, который вы разместили здесь точно так же, как он отступом в исходном файле? Похоже на синтаксическую ошибку в отступе. –

+0

2 мелочи, не связанные с вашей ошибкой: вы не должны использовать do-notation, если вы не используете монады, и я бы предпочел использовать функцию из stdlib для выполнения этой задачи (break/span/etc). – hugomg

+0

Да код от 1 до 1. Я знаю, что это, вероятно, от идентификации, но я не могу его найти. Кроме того, @missingno Я думаю, что понимаю, что вы имеете в виду, но, как я уже сказал, я пробовал много разных способов и не мог понять, где ошибка. Я думаю, что не использовал «делать» при моем первом запуске. – user3129475

ответ

2

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

getToNextSign :: String -> String 
getToNextSign str = do 
    let mathSigns = ['+' , '-' , '*' , '/' , '^' , ')'] 
    let a = head str 
    if a `elem` mathSigns 
    then str 
    else if tail str /= [] 
     then getToNextSign $ tail str 
     else [] 

main = do 
    putStrLn $ getToNextSign "(2sdwds+asd)+3" 

Как было отмечено в комментариях вам не нужно делать запись здесь, так как вы не используете монады. Операторы let могут быть записаны в виде операторов.

getToNextSign :: String -> String 
getToNextSign str = 
    if a `elem` mathSigns 
    then str 
    else if tail str /= [] 
     then getToNextSign $ tail str 
     else [] 
    where 
    a = head str 
    mathSigns = ['+' , '-' , '*' , '/' , '^' , ')'] 
+0

Действительно благодарю вас. Наверное, мне нужно будет проверить все места. Но это целая пустая строка, потому что это единственное отличие. – user3129475

+0

@ user3129475 Нет, это даже не близко к единственной разнице. Важное отличие состоит в том, что тело функции должно быть отступом. Вы не отступали, если, но это обязательно. – Carl

+1

@ StephenDiehl Я бы настоятельно рекомендовал третью версию, используя гвардии вместо вложенных ifs. Именно поэтому они и на языке. – Carl

3

Помимо улучшения форматирования, предоставленного Стивеном Дилем, есть и другие улучшения, которые вы можете сделать. Как Карл указывает, вы можете заменить, если-то еще, если-то еще с охраной, так как:

getToNextSign :: String -> String 
getToNextSign str 
    | a `elem` mathSigns = str 
    | tail str /= []  = getToNextSign $ tail str 
    | otherwise   = [] 
    where 
    a = head str 
    mathSigns = ['+' , '-' , '*' , '/' , '^' , ')'] 

Когда вы на него, вы также можете заменить голову/хвост с сопоставления с образцом, как и в

getToNextSign :: String -> String 
getToNextSign (c:cs) 
    | c `elem` mathSigns = c:cs 
    | not (null cs)  = getToNextSign cs 
    | otherwise   = [] 
    where 
    mathSigns = ['+' , '-' , '*' , '/' , '^' , ')'] 

, и если вы собираетесь делать сопоставление с образцом, вы можете так же хорошо пройти его.

getToNextSign :: String -> String 
getToNextSign str = case str of 
    c:_ | c `elem` mathSigns -> str 
    c:[] -> [] 
    _:cs -> getToNextSign cs 
    where mathSigns = ['+' , '-' , '*' , '/' , '^' , ')'] 

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

+0

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

+0

Это хорошо. Мы могли бы продолжить рефакторинг с помощью 'getToNextSign [] = []', а затем 'getToNextSign str @ (c: cs)' '| c \ 'elem \' mathSigns = str'' | в противном случае -> getToNextSign cs'. –

+0

@enoughreptocomment Я подумал об этом, но решил не включать его, потому что он представит еще одну (возможно) внешнюю концепцию - псевдоним '@' в шаблонах. – kqr

3

Здесь представлена ​​более простая альтернатива вашей проблеме с использованием функций списка прелюдии, dropWhile и elem, который имеет тип sig (a -> Bool) -> [a] -> [a]. Он принимает функцию, которая удаляет элементы из списка, если условие, предоставляемое функцией, истинно.

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

let getNextSign x = dropWhile (not . `elem` "+-*/^)") x 

Старайтесь избегать явной рекурсии, когда это возможно, и надевайте более высокие функции порядка. И Prelude имеет множество функций манипулирования списком, которые пригодились все время.

Смежные вопросы