Зачем использовать нулевую функцию вместо == [] для проверки пустого списка в Haskell?

Я читаю главу "Начинаем" в " Learn You a Haskell for Great Good"! , Это говорит:

null проверяет, является ли список пустым. Если это так, он возвращает True, в противном случае он возвращает False. Используйте эту функцию вместо xs == [] (если у вас есть список с именем xs)

Я попробовал в ghci:

xs = []      -- and then,

xs == []
null xs

Они оба True.

Интересно какая разница.

Должен ли я использовать null функцию вместо == [] и почему?

Ответ 1

Вы должны использовать null. В большинстве случаев это не имеет значения, но это хорошая привычка, так как иногда вы можете захотеть проверить, пуст ли список несопоставимых вещей. Вот короткий, четкий пример, показывающий эту разницу:

> null [id]
False
> [id] == []
<interactive>:1:1: error:
    • No instance for (Eq (a0 -> a0)) arising from a use of ‘==
        (maybe you haven't applied a function to enough arguments?)
    • In the expression: [id] == []
      In an equation for ‘it: it = [id] == []

Ответ 2

Есть разница. Чтобы использовать x == [], тип элементов списка должен быть членом класса типов Eq. Действительно, проверка равенства двух списков определяется объявлением экземпляра:

instance Eq a => Eq [a] where
    []     == []      =  True
    (x:xs) == (y:ys)  =  x == y  &&  xs == ys
    _      == _       =  False

Это означает, что вы не можете использовать x == [] если x является, например, списком IO Int s.

null :: [a] → Bool с другой стороны, использует сопоставление с образцом. Это реализовано как:

null                    :: [a] -> Bool
null []                 =  True
null (_:_)              =  False

Таким образом, независимо от типа элементов списка, он всегда проверяет тип.

Ответ 3

В дополнение к хорошим ответам, данным до сих пор, null самом деле имеет тип

null :: Foldable t => t a -> Bool

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

Это означает, что использование null на Map или в Set допустимо.

> null Map.empty
True
> null (Map.singleton 1)
False
> null Set.empty
True
> null (Set.singleton 1)
False
> null []
True
> null [1]
False

Я не думаю, что особенно часто пишут функции, которые должны быть такими общими, но по умолчанию не мешает писать более общий код.

Примечание стороны

Во многих случаях вы в конечном итоге захотите использовать такую функцию, как null для выполнения условного поведения в списке (или другой структуре данных). Если вы уже знаете, что ваши входные данные представляют собой определенную структуру данных, более элегантно просто сопоставить шаблон с пустым регистром.

сравнить

myMap :: (a -> b) -> [a] -> [b]
myMap f xs
  | null xs = []
myMap f (x:xs) = f x : myMap f xs

в

myMap' :: (a -> b) -> [a] -> [b]
myMap' f [] = []
myMap' f (x:xs) = f x : myMap' f xs

В общем, вы должны попытаться предпочесть сопоставление с образцом, если это имеет смысл.

Ответ 4

Также простая функция, которая фильтрует весь пустой список, не будет работать:

withoutEmpty = filter (== [])

и это будет:

withoutEmpty = filter null

заметить, что:

withoutEmpty ls = filter (==[]) ls

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

Также посмотрите на ответ @cole, он дополняет все ответы здесь, класс типов Foldable имеет реализованную здесь null функцию:

Чтобы увидеть больше информации складной здесь