2010-02-01 2 views
5

Здесь проблема заявление:различные решения для реализации Clojure задачи

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

Раствор длинный,

(defn large [x y] 
(if (> x y) x y)) 

(defn large-3 [x y z] 
(if(> (large x y) z) (large x y) z)) 

(defn small [x y] 
(if (< x y) x y)) 

(defn small-3 [x y z] 
(if (< (small x y) z) (small x y) z)) 

(defn second-largest [x y z] 
    (let [greatest (large-3 x y z) 
    smallest (small-3 x y z)] 
    (first (filter #(and (> greatest %) (< smallest %)) [x y z])))) 

(defn square [a] 
    (* a a) 
) 

(defn sum-of-square [x y z] 
    (+ (square (large-3 x y z)) (square (second-largest x y z)))) 

Просто хотелось бы знать, что различные/малообъемных пути эта проблема может быть решена в Clojure.

+0

Может ли кто-нибудь найти ленивое решение? Мне бы это понравилось. Мне пришлось бы как-то обойти этот вид. –

+0

У вас не может быть ленивого решения проблемы, которая по своей сути требует оценки всех входных данных, таких как сбор меньших двух три заданных числа. Однако ваш вопрос вдохновил меня на то, чтобы придумать слегка измененную версию проблемы - см. Мой ответ ниже для измененной проблемы и решения. –

ответ

3

(см версию последовательности задачи вместе с ленивой решения в моем втором обновлении до этого ответа ниже.)

(defn square [n] 
    (* n n)) 

;; generalises easily to larger numbers of arguments 
(defn sum-of-larger-squares [x y z] 
    (apply + (map square (take 2 (reverse (sort [x y z])))))) 

;; shorter; generalises easily if you want 
;; 'the sum of the squares of all numbers but n smallest' 
(defn sum-of-larger-squares [x y z] 
    (apply + (map square (drop 1 (sort [x y z]))))) 

Update:

Для расширения замечаний, полученных от выше , straighforward обобщение первой версии является следующее:

(defn sum-of-larger-squares [n & xs] 
    (apply + (map square (take n (reverse (sort xs)))))) 

Вторая версия прямолинейно обобщающий версии Артур писал в то же время:

(defn sum-of-larger-squares [n & xs] 
    (apply + (map square (drop n (sort xs))))) 

Кроме того, я видел точно такая же проблема, решаемая в схеме, возможно, даже на SO ... Она включала в себя некоторые забавные решения, как тот, который вычислил некоторые из всех трех квадратов, затем вычитал наименьший квадрат (это очень просто выразить с помощью примитивов Схемы). Это «неэффективно», потому что он вычисляет один дополнительный квадрат, но он, безусловно, очень читабельен. Кажется, не удается найти ссылку сейчас, к сожалению.

Update 2:

В ответ на комментарий Артура Ulfeldt по вопросу, в ленивым решения (надеюсь забавной) другой версии проблемы. Код первое объяснение ниже:

(use 'clojure.contrib.seq-utils) ; recently renamed to clojure.contrib.seq 

(defn moving-sum-of-smaller-squares [pred n nums] 
    (map first 
     (reductions (fn [[current-sum [x :as current-xs]] y] 
        (if (pred y x) 
         (let [z (peek current-xs)] 
         [(+ current-sum (- (* z z)) (* y y)) 
          (vec (sort-by identity pred (conj (pop current-xs) y)))]) 
         [current-sum 
         current-xs])) 
        (let [initial-xs (vec (sort-by identity pred (take n nums))) 
         initial-sum (reduce + (map #(* % %) initial-xs))] 
        [initial-sum initial-xs]) 
        (drop n nums)))) 

clojure.contrib.seq-utils (или c.c.seq) Lib есть для функции reductions. Вместо этого можно использовать iterate, но не без какой-либо дополнительной сложности (если только вы не захотите рассчитать длину seq чисел, которые должны быть обработаны в начале, что будет противоречить цели, которая может быть как можно более ленивой).

Объяснение с примером использования:

user> (moving-sum-of-smaller-squares < 2 [9 3 2 1 0 5 3]) 
(90 13 5 1 1 1) 

;; and to prove laziness... 
user> (take 2 (moving-sum-of-smaller-squares < 2 (iterate inc 0))) 
(1 1) 

;; also, 'smaller' means pred-smaller here -- with 
;; a different ordering, a different result is obtained 
user> (take 10 (moving-sum-of-smaller-squares > 2 (iterate inc 0))) 
(1 5 13 25 41 61 85 113 145 181) 

Как правило, (moving-sum-of-smaller-squares pred n & nums) генерирует ленивым SEQ сумм квадратов n ПРЕД-наименьшим числом в более длинных начальных фрагментов исходной последовательности SEQ чисел, где «PRED «самый малый» означает наименьший по отношению к упорядочению, вызванному предикатом pred. С pred = > рассчитывается сумма n наибольших квадратов.

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

С другой стороны, он выполняет большую сортировку; Я считаю, что не стоит пытаться оптимизировать эту часть, так как сортировка seqs всегда равна n элементам, и на каждом шаге выполняется не более одной операции сортировки (нет, если сумма не требует настройки).

+0

необходимо скомпоновать каждое число перед его суммированием. (+% 1% 1) (*% 2% 2)), чтобы сделать его более похожим (применить суммы квадратов ...) –

+0

Мне нравится ваше обобщение проблемы :) –

+0

Да, я забыл квадрат изначально, но исправил его как 15 секунд ... Вы быстро поймали меня на этом. :-) Что касается анонимной функции, которая суммирует и квадратизирует его аргументы, все в порядке, если вы возводите в квадрат два числа, но не можете использоваться с 'reduce'. Ну, если вы хотите решить эту проблему, это не так, это действительно хорошо подходит для решения другой проблемы. ;-) –

7

; почему только 3? как насчет N

(defn sum-of-squares [& nums] 
    (reduce + (map #(* % %) (drop 1 (sort nums))))) 

или если вы хотите «сумму величайших двух цифр:

(defn sum-of-squares [& nums] 
    (reduce + (map #(* % %) (take 2 (reverse (sort nums)))))) 

(взять 2 (обратный (сортировка Nums))) ответ fromMichał Marczyk в

+0

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

+0

Собственно, '(применить сначала [(iterate inc 0)]) 'и' (apply (fn [x & xs] (iterate inc 0)) 'оба дают 0 ... Кажется мне достаточно ленивым. :-) Кроме того,' apply' делает один вызов функции вместо множества вызовов функций 'сокращение'. Не то, что мне не нравится' сокращение' ... наоборот, я считаю это чрезвычайно полезным, и для вещей, таких как суммы, я использовал его взаимозаменяемо с 'apply' из-за противоречивых привычек Scheme/Haskell. Тем не менее мне трудно отдать предпочтение «применять», где оба будут делать. –

+0

Возможно, существуют различия в применении и уменьшении. Рассмотрим '(уменьшить str some-strings)' и '(применить str some-strings)'. последнее лучше, потому что внутри него используется StringBuilder. Такие вещи, как '+', оптимизированы в случае с двумя аргументами. Я не уверен, оптимизация применяется с «уменьшением», но это может иметь значение здесь. – kotarak

8
(defn foo [& xs] 
    (let [big-xs (take 2 (sort-by - xs))] 
    (reduce + (map * big-xs big-xs)))) 
.
+0

О, это круто ... Делает '(reverse (sort xs))' выглядит немного многословным. Я только что снова открыл «sort-by» (для использования в решении альтернативной версии проблемы, изложенной в моем ответе), и снова обнаружил, что это не совсем похоже на «sortBy» Haskell ..., который я скорее больше привык. Я несколько минут пробормотал проклятия, потом заметил это - и у него все получилось. +1 для этого. –

Смежные вопросы