Тестирование предикат равенства, который возвращает общий элемент


Я собираюсь попробовать перебора решение магический квадрат. Чтобы упростить код позже, я хотел ту же самую функцию =, но тот, который возвращает общий элемент, если все элементы равны и nil в противном случае. Это в отличие от = которая всегда возвращает либо true или false. Я называю это =? из-за отсутствия лучшего название.

Пример:

(=? 1 2 1)
nil ; nil because they're not all the same

(=? 1 1 1)
1 ; Returns the common element of 1

Очевидно, эта функция не будет работать хорошо, когда nil может быть единственным элементом в коллекции, так как он будет возвращать falsey даже если все элементы одинаковы, но nil.

Я думал, что там должен быть какой-то core операции я мог бы использовать здесь, но после нескольких минут размышления, я не мог вспомнить ни одного. Я в конечном итоге прокатки мои собственные функции, используя loop. Я хотел бы общие советы на функцию, которую я написал, или потенциально более идиоматические способ, чтобы написать это, что я упускаю. Это выглядит чрезвычайно неуклюжим для меня, особенно после того, как я осознал необходимость last-arg аккумулятора. Рекурсивное решение будет приятно, так как я боюсь, что я упускаю что-то очевидное:

(defn =?
  "Checks if every supplied argument =s every other.
  Returns the common element if they're all the same, nil otherwise."
  [& args]
  (loop [[f-arg & r-args] (rest args)
         last-arg (first args)]
    (cond
      (nil? f-arg) last-arg
      (= f-arg last-arg) (recur r-args f-arg)
      :else nil)))


89
1
задан 9 февраля 2018 в 09:02 Источник Поделиться
Комментарии
1 ответ

Вскоре после того, как я выложил это, я пошла прогуляться и поняла, что это прекрасная возможность использовать reduce. Мне как-то стыдно, что я не видел его изначально, но это мои мысли, которые приводят меня к тому, что я намного счастливее.

Во-первых, я попробовал простое сокращение. Это была довольно простая, хотя все еще немного многословен:

(defn =?2 [& args]
(reduce (fn [acc n]
(if (= n acc)
n
(reduced nil)))
(first args)
(rest args)))

Я решил, что это было, вероятно, достаточно просто использовать макрос-функции для. Обычно я не люблю использовать их для сокращения, но там не так много происходит здесь:

(defn =?3 [& args]
(reduce #(if (= % %2) % (reduced nil))
(first args)
(rest args)))

Я не люблю использовать явные вызовы first и rest, как я считаю, они обычно аккуратнее, когда они подразумеваются в деконструкции. Я решил разобрать аргументы:

(defn =?4 [& [arg & rest-args]]
(reduce #(if (= % %2) % (reduced nil))
arg
rest-args))

Затем я проверил, что = использует, и выяснили, что он просто использует несколько списков аргументов, поэтому я попытался это. Это кажется мне более идиоматические решение:

(defn =?5
([] nil)
([arg] arg)
([arg & args]
(reduce #(if (= % %2) % (reduced nil))
arg
args)))

Я разрываюсь между 4 и 5, но я думаю, что оба являются значительное улучшение.

2
ответ дан 9 февраля 2018 в 11:02 Источник Поделиться