Clojure: вопрос для начинающих о передаче векторизованной строки в функцию - PullRequest
0 голосов
/ 14 апреля 2020

Я новичок в Clojure. Я пытался получить следующий код для генерации анаграмм слова «крыса». В идеале я хотел бы начать со строки, преобразовать ее в вектор - но сейчас я попробовал начать с вектора. Я перепробовал много вариантов и получал ошибки компилятора. Я понимаю, что означают ошибки, но не уверен, что нужно делать в ближайшем будущем, чтобы исправить их. Текущий код выдает эту ошибку: «clojure.lang.PersistentVector не может быть приведен к clojure.lang.IAtom».

(ns clojure.examples.anagram
(:gen-class))

(defn generatePermutations [v n] 
   (if (zero? n) 
      (do (println (apply str v)) v) 

      (loop [i 0 a v] 
         (if (< i n) 
            (do 
               (let [a (generatePermutations a (dec n))] 
               (recur (inc i) (swap! a (if (even? n) 0 i) n)))) 
            (generatePermutations a (dec n)))))) 

(let [word (vec ['r' 'a' 't'])]
   (generatePermutations word 3))

Заранее благодарим за любую помощь!

Вот версия java кода. Эта версия принимает слово «крыса» (или любое другое слово) в качестве аргумента командной строки. Для версии Clojure я запускал ее в онлайн-среде IDE, и не было простого способа передать аргумент, поэтому я пытался жестко закодировать ввод.

public class Anagram {
    public static void main(String[] args) {
        if (args.length !=1) {
            System.err.println("Exactly one argument is required");
            System.exit(1);
        }
        String word = args[0];
        generatePermutations(word.toCharArray(),word.length()-1);
    }

    private static void generatePermutations(char[] a, int n){
        if (n ==0){
            System.out.println(String.valueOf(a));
        } else {
            for (int i = 0; i < n; i++) {
                generatePermutations(a, n-1);
                swap(a,n%2 == 0?0: i, n);
            }
            generatePermutations(a,n-1);
        }
    }

    private static void swap (char[] a, int i, int j){ 
        char saved = a[i];
        a[i]= a[j];
        a[j] = saved;
    }
}

Ответы [ 2 ]

1 голос
/ 14 апреля 2020
Execution error (ClassCastException) at user/generatePermutations (REPL:55).
clojure.lang.PersistentVector cannot be cast to clojure.lang.IAtom

проблема, как говорится в описании ошибки, заключается в том, что вы пытаетесь вызвать swap! для значения не атома. Чтобы «обновить» вектор, лучше использовать assoc. Так что, в вашем случае, просто заменив swap! на assoc, вы получите его.

вы можете прочитать больше о том, что такое atom и как его использовать здесь

обновление

приведенный пример показывает мою ошибку: своп! должен был поменять 2 элемента в векторе.

это можно сделать следующим образом:

(defn swap [data-v i j]
  (assoc data-v
         i (data-v j)
         j (data-v i)))

, поэтому ваша начальная функция работает (до некоторой степени):

user> (generatePermutations (vec "rat") 3)
;; rat
;; art
;; tra
;; rta
;; atr
;; tar
Execution error (IndexOutOfBoundsException) at user/swap (REPL:1423).
null

сбой при попытке вызова (swap a 0 3).

обновление 2

Другая проблема заключается в том, что вы вызываете (generatePermutations (vec "rat") 3), тогда как в java версии вы делаете это с word.length() - 1. и действительно, (generatePermutations (vec "rat") 2) дает ожидаемый вами результат.

Хотя сейчас это выглядит правильно, я думаю, это не совсем «закрытый» способ решения такого рода задач.

0 голосов
/ 17 апреля 2020

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

(defn except-nth [coll idx]
  (let [v (vec coll)]
    (if (< idx (count v))
      (vec (concat (subvec v 0 idx) (subvec v (inc idx))))
      v)))

(defn permutation [v]
  (if (<= (count v) 1)
    (list v)

    (apply
     concat
     (for [idx (range (count v))]
       (map
        (fn [sub-permut] (vec (cons (get v idx) sub-permut)))
        (permutation (except-nth v idx))
        )))))

Функция except-nth - это просто вспомогательная функция. Возвращает вектор без элемента в позиции idx. Например:

(except-nth (range 4) 1)
;; => [0 2 3]

Функция permutation берет коллекцию и возвращает список всех возможных перестановок. Вот несколько примеров результатов:

(permutation [42])
;; => ([42])

(permutation (vec (range 3)))
;; => ([0 1 2] [0 2 1] [1 0 2] [1 2 0] [2 0 1] [2 1 0])

(permutation (vec "ab"))
;; => ([\a \b] [\b \a])

(permutation ["foo" "bar" 42])
;; => (["foo" "bar" 42] ["foo" 42 "bar"] ["bar" "foo" 42] ["bar" 42 "foo"] [42 "foo" "bar"] [42 "bar" "foo"])

Для новичка одной из важных вещей является создание локальной среды REPL. Это поможет поиграться с небольшим кодом и изучить новые. Исходя из Java, код permutation может сбивать с толку по следующим причинам:

  • это рекурсивная функция
  • нетривиальное использование map function
  • ключевое слово for здесь относится не к «обычному» для l oop, а к синтаксису понимания списка
  • использованию функции более высокого порядка apply

По всем этим причинам важно играть в REPL. Если вам нужны ресурсы для быстрого и забавного обучения, я настоятельно рекомендую Clojure для Храброго и Истинного и выполнить некоторые упражнения на веб-сайте 4Clojure . Вы также можете найти много видео о разрешении / обсуждении проблемы 4Clojure на Practicalli канале YouTube .

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