Заимствование enumerate
хорошо и поощряется. Однако это можно сделать немного ленивее, отказавшись вычислить длину аргумента:
enumerate = zip [0..]
(На самом деле, обычно стоит просто использовать zip [0..]
, не называя его enumerate
.) Мне непонятно, почему вы считаете, что ваш второй пример должен быть более дорогостоящим во времени или пространстве. Помните: индексирование - это O (n), где n - это индекс. Ваша жалоба на громоздкость fst
и snd
обоснована и может быть исправлена путем сопоставления с образцом:
validQueens' xs = and [abs (y - x) /= j - i | (i, x) <- l, (j, y) <- l, i < j]
where l = zip [0..] xs
Теперь вы можете быть немного обеспокоены эффективностью этого двойного цикла, так как пункт (j, y) <- l
будет проходить по всему позвоночнику l
, когда на самом деле мы просто хотим, чтобы он начинался там, где мы оставили с (i, x) <- l
. Итак, давайте напишем функцию, которая реализует эту идею:
pairs :: [a] -> [(a, a)]
pairs xs = [(x, y) | x:ys <- tails xs, y <- ys]
Сделав эту функцию, ваша функция не слишком сложна для адаптации. Извлекая предикат в его собственную функцию, мы можем использовать all
вместо and
:
validSingleQueen ((i, x), (j, y)) = abs (y - x) /= j - i
validQueens' xs = all validSingleQueen (pairs (zip [0..] xs))
Или, если вы предпочитаете бессмысленную запись:
validQueens' = all validSingleQueen . pairs . zip [0..]