Как да избегнете извикване на функция два пъти в (повтаряне ..)

Моля, разгледайте този блок от код.

(loop [k from res '()]
    (if (< (count res) n)
      (recur (next-num k)  (conj res (next-num k)))
      (sort res)))

Сега да предположим, че функцията (next-num k) прави някакво скъпо изчисление. Не можем да си позволим да се обадим два пъти. Каква ще е алтернативата? Нов съм в Clojure и не знам много основни функции. Но съм сигурен, че трябва да има начин.


person Adeel Ansari    schedule 18.12.2013    source източник


Отговори (2)


Използвайте let:

(loop [k from res '()]
    (if (< (count res) n)
      (let [the-next-num (next-num k)]
          (recur the-next-num  (conj res the-next-num)))
      (sort res)))
person Nathan Davis    schedule 18.12.2013
comment
Или избягвайте изцяло цикъла: (->> (iterate next-num from) rest (take n) sort) - person Beyamor; 18.12.2013
comment
@Beyamor: Вашето предложение работи като чар. Но нямам идея защо. Бихте ли разяснили, вероятно в отделен ваш отговор. - person Adeel Ansari; 18.12.2013
comment
Възстанових отговора си и обясних кода без цикъл, но @Nathan определено ме победи с използването на let. - person Beyamor; 18.12.2013

Както каза @NathanDavis, let ви позволява да наименувате междинни стойности:

(loop [k from res '()]
  (if (< (count res) n)
    (let [next-k (next-num k)]
      (recur next-k (conj res next-k)))
    (sort res)))

Въпреки това, преди да посегнете към loop, си струва да видите дали можете да съставите основни функции със същия ефект. Често можете да напишете нещо по-малко сложно и да изложите важните подробности.

Същността на кода се отнася до изграждането на последователност от повтарящи се приложения на next-num. За щастие има основна функция, която прави това: iterate. Използвайки iterate, можем да създадем безкрайна мързелива последователност от стойности:

(iterate next-num from)
; => (from, from', from'', ...)

Ние обаче не искаме първата от тези стойности. Можем да получим останалата част от последователността с, добре, rest:

(rest
  (iterate next-num from))
; => (from', from'', from''', ...)

В този момент можем да вземем n стойности с take:

(take n
  (rest
    (iterate next-num from)))
; => (from', from'', from''')

И накрая, можем да сортираме тези n стойности:

(sort
  (take n
    (rest
      (iterate next-num from))))
 ; => (from'', from', from''')

Разбира се, дълбоко вложените извиквания на функции скоро стават неудобни. Макросът за нишки ->> (като своя брат ->) е малко синтактична захар, която ни позволява да пренаредим кода си в нещо малко по-хубаво:

(->>
  (iterate next-num from)
  rest
  (take n)
  sort)

И така, можете да видите как една мощна библиотека от функции за манипулиране на последователност ни позволява да се отдалечим от зациклянето на ниско ниво.

person Beyamor    schedule 18.12.2013
comment
Страхотно обяснение, наистина. Въпреки че приех другия отговор, тъй като let всъщност и точно е отговорът на моя въпрос. Но този подход определено и недвусмислено е по-добър. - person Adeel Ansari; 18.12.2013