2014-09-16 3 views
3

Я пытаюсь получить второй элемент в списке в Haskell. Я бы предположил, что лучший способ сделать это - получить голову хвоста списка.Второй самый последний элемент списка Haskell

secondMostRecentChoice :: History -> Choice // a choice is just a Bool like Bake | NoBake 
secondMostRecentChoice [] = "Not Enough History" 
secondMostRecentChoice history = 
    if (length history == 1) 
     then "Still Not Enough History" 
    else if (length history >= 2) 
     then (head [b | (head a,b) <- history]) //Here is the problem 
     else "Not supposed to be here" 
    else "Not supposed to be here" 

, но я получаю следующее: ошибка

Разбор в схеме: голова
Возможно вызвано недостающий 'делать'?

Почему мне нужно сделать do или это ложное предположение?

+0

Да, это ложное предложение. вы можете написать то, что хотите, как 'head [b | (a: b: _) <- history] '-' a' уже является «голова». Но вы должны * никогда не измерять «длину» списка, если можете его избежать. И здесь вы можете избежать этого, просто определив свою функцию как совпадение с шаблоном (a.o.t. ваше использование соответствия шаблону внутри понимания списка), как предлагают некоторые ответы. –

ответ

5

Я хотел бы использовать сопоставление с образцом, как это ясно, что виды отказов:

second :: [a] -> a 
    second []  = error "Empty list" 
    second [x]  = error "Singleton list" 
    second (_:x:_) = x 

Яснее, нет? Делает спецификацию очевидной.

+0

Разве это не факт, что 'head'' '[a] -> a', а не' Maybe'' надзор? Я удивлен, увидев вас в этом случае «ошибка». –

+0

@BartekBanachewicz Это просто означает, что если список слишком мал, это вызовет исключение, которое может быть захвачено только в монаде IO. Хотя это дает гораздо лучшую отчетность об ошибках, чем «Может быть». – randomusername

+1

Почему бы не «Либо String a», ни какой-нибудь общий «MonadError»? Я не хотел бы слишком сильно перетаскивать эту ветку комментариев, но это кажется озадачивающим. –

9

Самый короткий способ сделать это - просто совместить шаблон.

secondElem :: [a] -> Maybe a 
secondElem (_:x:_) = Just x 
secondElem _  = Nothing 
0

Самый простой способ получить второй пункт - использовать оператор infix !!. Он позволяет вам получить доступ к определенному элементу в списке. Обычно для большинства списков это будет слишком медленно, но поскольку это только второй элемент, это не имеет большого значения.

+0

Если список меньше 2 элементов, это приведет к запутанному сообщению об ошибке. – randomusername

+0

Я имел в виду, чтобы он использовал его после того, как он определил, что список был как минимум 2 элемента. – Kytuzian

+0

Но как вы это делаете, не проходя длину списка (добавляя бесполезные служебные данные), как это происходит с функцией 'length'? – randomusername

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