Второй самый последний элемент списка 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"

но я получаю следующее:

Ошибка синтаксического анализа в шаблоне: head
Возможно, из-за отсутствия 'do'?

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


person chris Frisina    schedule 16.09.2014    source источник
comment
да это ложное предположение. можно было написать что хотел, так как head [b | (a:b:_) <- history]a это уже голова. Но вы не должны никогда измерять length списка, если можете этого избежать. И здесь вы можете избежать этого, просто определив свою функцию как сопоставление с образцом (помимо использования совпадения с образцом внутри понимания списка), как предполагают некоторые ответы.   -  person Will Ness    schedule 16.09.2014


Ответы (3)


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

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

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

person Don Stewart    schedule 16.09.2014
comment
Не является ли упущением тот факт, что head является [a] -> a, а не Maybe a? Я немного удивлен, увидев вас error в таком случае. - person Bartek Banachewicz; 16.09.2014
comment
@BartekBanachewicz Это просто означает, что если список слишком мал, это вызовет исключение, которое может быть перехвачено только в монаде IO. Хотя это дает гораздо лучшие отчеты об ошибках, чем Maybe a. - person randomusername; 16.09.2014
comment
Почему тогда не Either String a или что-то общее MonadError? Я бы не хотел слишком затягивать эту ветку комментариев, но это кажется озадачивающим. - person Bartek Banachewicz; 16.09.2014
comment
@BartekBanachewicz Я думаю, это просто то, что было проще напечатать в поле ответа SO. Его можно легко реорганизовать с помощью s/error/fail/ и s/= x/= return x/. - person randomusername; 16.09.2014
comment
Нет никакой спецификации. Использование сопоставления с образцом позволяет понять, где находятся пробелы в спецификации. Эти дыры могут быть заполнены «Или», или «Может быть». - person Don Stewart; 16.09.2014

Самый короткий способ сделать это — просто сопоставить его с образцом.

secondElem :: [a] -> Maybe a
secondElem (_:x:_) = Just x
secondElem _       = Nothing
person randomusername    schedule 16.09.2014

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

person Reed Oei    schedule 16.09.2014
comment
Если список меньше 2 элементов, это выдаст запутанное сообщение об ошибке. - person randomusername; 16.09.2014
comment
Я хотел, чтобы он использовал его после того, как определил, что список состоит как минимум из 2 элементов. - person Reed Oei; 16.09.2014
comment
Но как это сделать, не проходя весь список (добавляя бесполезные накладные расходы), как это произошло бы с функцией length? - person randomusername; 16.09.2014
comment
Неплохо подмечено. Я, конечно, не эксперт в этом, но мне просто показалось, что самый простой способ получить второй элемент — это использовать оператор для получения второго элемента. - person Reed Oei; 16.09.2014
comment
Это правда. Это определенно самое простое (одна строка вместо двух или трех других решений). - person randomusername; 16.09.2014