Почему foldl1 не может применить оператор (==)?

Из прелюдии:

foldl1: он берет первые 2 элемента списка и применяет к ним функцию, затем передает функции этот результат и третий аргумент и так далее.

Почему нельзя написать что-то подобное?

foldl1 (==) [6, 6, 6]
foldl1 (\x y -> x == y) [6, 6, 6]

person gremo    schedule 06.02.2011    source источник
comment
Просто догадка, но применение оператора равенства к первым двум дает логическое значение. Я сомневаюсь, что имеет смысл сравнивать логическое значение с целым числом.   -  person Jeff Mercado    schedule 06.02.2011


Ответы (4)


РЕДАКТИРОВАТЬ: Антал указывает, что мои рассуждения были неверными. Вот соответствующая часть комментария, в котором дается реальное рассуждение (мне неудобно принимать это дословно, но этот ответ был принят, поэтому я не могу его удалить):

Причина, по которой это не работает, заключается в том, что тип foldl1 — это (a -> a -> a) -> [a] -> a, а тип (==) — это Num a => a -> a -> Bool. Поскольку Bool не является Num, тип (==) не соответствует a -> a -> a, поэтому заявка foldl1 отклоняется. Если бы это было принято, вы бы оказались в ситуации, когда вы пытаетесь сделать True == 6, но система типов никогда не заведет вас так далеко.

Оригинальный ответ (последнее рассуждение неверно):

== возьмет два Int и вернет Bool. После первой итерации ваш примерный список становится [True, 6]. Затем он пытается сравнить True с 6, что не удается.

person moinudin    schedule 06.02.2011
comment
Ваше первое предложение верно, но True не приводится к 1 в Haskell! - person dvitek; 06.02.2011
comment
@marcog: спасибо, это имеет смысл. Позор мне не думать об этом! :) - person gremo; 06.02.2011
comment
@drvitek Спасибо. Мой практический haskell не силен. - person moinudin; 06.02.2011
comment
marcog: Не беспокойтесь — это хорошая заметка, если OP когда-нибудь воссоздаст эту функциональность на другом языке и задается вопросом, почему, например, этот код дает неверные результаты на [6,6,1]. Я бы не хотел отслеживать этот баг... - person dvitek; 06.02.2011
comment
Это все еще не совсем правильно. Список [True,6] не может существовать в Haskell, потому что он плохо типизирован; даже если бы это было возможно, это привело бы к сбою во время выполнения, а не во время компиляции. Причина, по которой это не работает, заключается в том, что тип foldl1 — это (a -> a -> a) -> [a] -> a, а тип (==) — это Num a => a -> a -> Bool. Поскольку Bool не является Num, тип (==) не соответствует a -> a -> a, поэтому заявка foldl1 отклоняется. Если бы это было принято, вы бы оказались в ситуации, когда вы пытаетесь сделать True == 6, но система типов никогда не позволит вам зайти так далеко. - person Antal Spector-Zabusky; 06.02.2011
comment
@Antal Спасибо за это объяснение. Жаль, что ОП только что принял мой ответ, потому что теперь я не могу его удалить. Поэтому я добавил ваши рассуждения почти дословно. Если вы опубликуете свой комментарий как отдельный ответ, я отмечу это модом для удаления, если это возможно, чтобы вы получили признание. - person moinudin; 06.02.2011
comment
@marcog: Не беспокойтесь об этом — ответ есть прямо сейчас, и это важно. - person Antal Spector-Zabusky; 07.02.2011

Если вы хотите проверить, равны ли все элементы списка, быстрое решение:

allEqual [] = True --(edit: this case is not necessary as pointed out by sepp2k)
allEqual xs = all (== head xs) xs

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

Редактировать: спасибо sepp2k за предложения.

person dvitek    schedule 06.02.2011
comment
Нет необходимости в первом случае, так как and [] все равно верно. Также and . map это all. - person sepp2k; 06.02.2011
comment
sepp2k: Я обновил после того, как понял это, не увидев сначала ваш комментарий. Думаю, мы думали об одном и том же. Однако разумно понять, что первый случай не нужен; Я думал, что head в этом случае не получится - person dvitek; 06.02.2011
comment
Было бы, если бы его оценили. Но если xs пусто, all никогда не вызывает функцию, поэтому head не нужно вычислять. - person sepp2k; 06.02.2011
comment
@ sepp2k: Да, я понял это после того, как некоторое время ломал голову над вашим комментарием. Похоже, мне еще предстоит полностью позволить ленивому мышлению взять верх... - person dvitek; 06.02.2011

Вот еще одна версия:

allEqual xs = and $ zipWith (==) xs (tail xs)
person Landei    schedule 06.02.2011
comment
Вдохновленный вашим ответом, есть еще это: allEqual xs = tail xs == init xs - person Dan Burton; 07.02.2011
comment
@Dan: Возможно, вы захотите использовать drop 1 вместо tail, так что это будет работать и с пустыми списками. - person Axman6; 09.02.2011
comment
Не поможет, потому что в этом случае происходит сбой при вызове init. - person Landei; 09.02.2011

Если вы хотите использовать складку, я бы предложил эту модификацию:

allEqual xs = foldr (\x acc -> x == head xs && acc) True xs

Это очень похоже на уже предложенный подход all. Обратите внимание, что и этот подход, и подход all могут работать с бесконечными списками (если ответ равен False).

Для очень длинных конечных списков в очень редких случаях вы можете получить лучшую производительность от строгого левого сгиба:

allEqual xs = foldl' (\acc x -> acc && x == head xs) True xs

Но правильное сгибание обычно лучше подходит для этой задачи, так как (в этой реализации) оно выполняет только количество шагов сворачивания, равное length $ takeWhile (== head xs) xs. Левая фальцовка каждый раз будет выполнять length xs шагов фальцовки.

person Dan Burton    schedule 06.02.2011
comment
Вы можете написать allEqual xs = foldl' (flip $ (&&).(== head xs)) True xs для лучшего запутывания. - person Landei; 07.02.2011