Использование метода, определенного в другом методе - PullRequest
2 голосов
/ 15 сентября 2011

Я работаю над решением для этого коана , в котором говорится:

Write a function which replicates each element of a sequence a variable number of times.

Чтобы сделать это, я хочу:

  1. Сделатьметод, который принимает последовательность и количество повторений каждого элемента.
  2. Определите локальный метод в этом методе, который дублирует значение v, n раз в последовательности.

Имея это в виду, я написал следующий метод:

(fn dupSeqX [aseq x]
  (fn dupx [v x]
    (if (= x 1) (list v) (concat v (dupx v (- x 1))))
  )
  (reverse (reduce #(concat %1 (dupx %2 x)) '() aseq)))

При запуске этого кода я получаю следующую ошибку:

java.security.PrivilegedActionException: java.lang.Exception: Unable to resolve symbol: dupx in this context (NO_SOURCE_FILE:0)

Как мне создать локальный метод, которыйПозволит ли мне закончить этот коан?

Есть ли какой-нибудь способ "clojure-esque" сделать это, о котором я не знаю?

Ответы [ 2 ]

8 голосов
/ 15 сентября 2011

Прежде всего, мы не говорим о методах: мы говорим о функциях. В clojure есть нечто, что вы могли бы вызвать метод, но это отличается от функции. Если вы прекратите использовать OO lingo, вы также потеряете стиль мышления OO.

То, что вы пытаетесь сделать, возможно. В основном вы хотите создать новую функцию с именем dupx в функции dupseqx. Сейчас вы создаете функцию, а затем выбрасываете ее (вы ничего не делаете с возвращаемым значением, и возвращается только последняя форма в функции). Поскольку функция похожа на любое другое значение, вы можете использовать тот же механизм, что и для любого другого значения: создать локальную «переменную». Какой механизм для этого? Это локальная привязка, и она работает следующим образом (имя в fn просто так, что вы можете вызывать его от себя; оно не должно совпадать с именем let):

(let [dupx (fn dupx [v x] 
             (if (= x 1) 
                  (list v) 
                  (cons v (dupx v (dec x)))))]
  (dupx 5 3))

Обратите внимание, что я исправил некоторые другие вещи.

Более короткая форма этого (исправление двойного имени уродства):

(letfn [(dupx [v x] (if (= x 1)
                       (list v) 
                       (cons v (dupx v (dec x)))))]
  (dupx 5 3))

Хорошо, во всем, что между "(let [...]" и соответствием ")", теперь у нас есть dupx функция.

Итак, теперь остальная часть вашего кода работает:

(fn dupSeqX [aseq x]
  (letfn [(dupx [v x] (if (= x 1) (list v) (cons v (dupx v (dec x)))))]
    (reverse (reduce #(concat %1 (dupx %2 x)) '() aseq))))

Этот код можно сделать немного более идиоматическим:

  • Правила кодирования: имя параметра coll вместо aseq
  • Рекомендации по кодированию: DoNotUseCamalCase сделай как это
  • Рекурсия, когда вам не нужно, это плохо для производительности и больших чисел.
  • Вы заново изобретаете колесо. Это хорошо для изучения кодирования, но не очень хорошо, если вы хотите узнать язык и стандартную библиотеку.

Как я это написал?

Первый основной фн. coll является стандартом для функции именования, которая ожидает последовательности.

(fn [coll times]  )

Если вы читаете этот «каждый элемент последовательности», ваш мозг должен пройти MAP.

(fn [coll times] 
   (map (fn ....) coll))

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

(fn [coll times] 
   (map (fn [val] (repeat times val)) coll))

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

 (fn [coll times] 
   (apply concat (map (fn [val] (repeat times val)) coll)))

Вы часто будете видеть шаблон (apply concat (map ....)). Для этой цели есть стандартная функция в стандартной библиотеке mapcat, и я сделаю внутреннюю функцию коротким синтаксисом.

 (fn [coll times] 
   (mapcat #(repeat times %) coll))

Надеюсь, это поможет!

2 голосов
/ 15 сентября 2011

Clojure имеет много или очень полезных функций, которые помогут вам привыкнуть к «мышлению в секвенциях».Когда вы обнаружите, что вы пишете что-то, что перебирает список, подумайте: «Могу ли я сопоставить это?», И когда вы обнаружите, что делаете что-то еще со списком, посмотрите этот список полезных функций seq .

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

вот подсказка, чтобы начать

(flatten (map #(repeat 3 %) [1 2 3 4]))
(1 1 1 2 2 2 3 3 3 4 4 4)

пс: #(repeat 3 %) является сокращением для (fn [n] (repeat 3 n))

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...