Как по-настоящему перемешать последовательность в Clojure? - PullRequest
0 голосов
/ 09 ноября 2019
(defn shuffle-letters 
  [word]
  (let [letters (clojure.string/split word #"")
        shuffled-letters (shuffle letters)]
  (clojure.string/join "" shuffled-letters)))

Но если вы введете "test", вы можете иногда возвращать "test".

Как изменить код, чтобы быть уверенным, что вывод никогда не будет равен вводу.

Я чувствую себя неловко, я могу легко решить это в Python, но Clojure настолько отличается от меня ...

Спасибо.

PS Я думаю, мы можем закрыть тему сейчас .. На самом деле loop - это все, что мне нужно ...

Ответы [ 3 ]

0 голосов
/ 09 ноября 2019

Вот еще одно решение (с использованием преобразователей):

 (defn shuffle-strict [s]
  (let [letters (seq s)
        xform (comp (map clojure.string/join)
                 (filter (fn[v] (not= v s))))]
    (when (> (count (into #{} letters)) 1)
      (first (eduction xform (iterate shuffle letters))))))

 (for [_ (range 20)]
         (shuffle-strict "test"))
 ;; => ("etts" "etts" "stte" "etts" "sett" "tste" "tste" "sett" "ttse" "sett" "ttse" "tset" "stte" "ttes" "ttes" "stte" "stte" "etts" "estt" "stet")

(shuffle-strict "t")
;; => nil

(shuffle-strict "ttttt")
;; => nil

Мы в основном создаем ленивый список возможных перемешиваний, а затем мы берем первое из них, отличное от ввода. Мы также следим за тем, чтобы на входе было как минимум 2 разных символа, чтобы не зависать (мы возвращаем nil здесь, поскольку вы не хотите, чтобы входная строка была в качестве возможного результата).

0 голосов
/ 10 ноября 2019

Если вы хотите, чтобы ваша функция возвращала последовательность:

(defn my-shuffle [input]
  (when (-> input set count (> 1))
    (->> input
         (iterate #(apply str (shuffle (seq %))))
         (remove #(= input %)))))
(->> "abc" my-shuffle (take 5))
;; => ("acb" "cba" "bca" "acb" "cab")

(->> "bbb" my-shuffle (take 5))
;; => ()
0 голосов
/ 09 ноября 2019

Вы можете использовать loop. Когда перетасованные буквы совпадают с оригиналом, recur возвращаемся к началу loop:

(defn shuffle-letters [word]
  (let [letters (clojure.string/split word #"")]
    (loop []  ; Start a loop
      (let [shuffled-letters (shuffle letters)]
        (if (= shuffled-letters letters)  ; Check if they're equal
          (recur)  ; If they're equal, loop and try again
          (clojure.string/join "" shuffled-letters))))))  ; Else, return the joined letters

Есть много способов, которыми это можно написать, но я думаю, чтоэто становится. Вы также можете избавиться от loop и сделать shuffle-letters рекурсивным. Это приведет к ненужной работе, хотя. Вы также можете использовать let-fn для создания локальной рекурсивной функции, но в этот момент, скорее всего, loop будет чище.


Что следует отметить:

  • Очевидно, что если вы попытаетесь перетасовать что-то вроде "H" или "HH", оно застрянет и зациклится навсегда, поскольку никакое количество перетасовок не заставит их отличаться. Вы можете сделать проверку раньше времени или добавить параметр к loop, который ограничивает количество попыток.

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

  • Вызов split не нужен. Вы можете просто позвонить vec на строку:

    (defn shuffle-letters [word]
      (let [letters (vec word)]
        (loop []
          (let [shuffled-letters (shuffle letters)]
            (if (= shuffled-letters letters)
              (recur)
              (clojure.string/join "" shuffled-letters))))))
    
...