Как перебрать список и составить списки элементов - PullRequest
0 голосов
/ 01 октября 2019

Я пытаюсь преобразовать логические функции в clojure. Я хочу, чтобы пользователь мог набрать (convert '(and x y z) для получения (nor (nor x) (nor y) (nor z). Итак, я создаю список с первым элементом nor, а затем пытаюсь создать остальные списки элементов, которые создаются при прохождении цикла for. Однако цикл for просто объединяет все списки и сохраняет nor вне его. Я также хочу знать, как пропустить первый элемент в списке, но это не мой приоритет сейчас. Я немного новичок в замыкании и не могу понять, как просто вернуть все списки, чтобы поместить их в большой список. Функции not и or не связаны с проблемой.

(defn lookup
  "Look up a value, i, in map m and returns the result if it exists. 
  Otherwise returns i."
  [i m]
  (get m i i))

(defn makelist
    [l]
    (for[i[l]] (list 'nor i)))

(defn convert
  [l]
  (let [p1 (first l)]
    (cond
      (= p1 'not) (map (fn [i] (lookup i '{not nor})) l)
      (= p1 'or) (list 'nor (map(fn [i] (lookup i '{or nor})) l))
      (= p1 'and) (list 'nor (makelist l))
      :else (print "error"))))

Вывод, который я получаю (nor ((nor (and x y z)))). Я хочу получить вывод (nor (nor and) (nor x) (nor y) (nor z). Я тоже не хочу (nor and), но пока не смогу выяснить, как пропустить первый элемент, я просто хочу иметь возможность разделять списки.

Ответы [ 2 ]

2 голосов
/ 01 октября 2019

Я вижу две проблемы:

  1. makelist имеет (for [i [l]] ...), поэтому он создает только один элемент, где i привязан ко всему входящему списку l - здесь вы хотите (for [i l] ...), чтобы обрабатывать каждый элемент l, предложение
  2. convert для and создает список с двумя элементами: nor ирезультат (makelist l) - здесь вы хотите получить (cons 'nor (makelist l)), чтобы вы получили список с nor в качестве первого элемента, а затем со всеми элементами результата вызова makelist.

Я не проверял две другие части convert, чтобы увидеть, есть ли у вас похожие ошибки, но с двумя вышеуказанными изменениями (convert '(and x y z)) выдаст (nor (nor and) (nor x) (nor y) (nor z))

0 голосов
/ 01 октября 2019

просто для удовольствия: я бы мысленно расширил и обобщил вашу задачу на переписывание структур данных в соответствии с некоторыми правилами, чтобы вы могли объявлять (возможно, рекурсивные) правила перезаписи для преобразования любого ввода в любой желаемый вывод в целом. (и на практике clojure)

давайте начнем с простой функции преобразования:

(defn convert [rules data]
  (if-let [res (some (fn [[condition rewrite]]
                       (when (condition data) (rewrite data)))
                     rules)]
    res
    data))

она находит первое правило, которое соответствует вашему вводу (если есть), и применяет его функцию преобразования:

(def my-rules [[sequential? (fn [data] (map #(convert my-rules %) data))]
               [number? inc]
               [keyword? (comp clojure.string/upper-case name)]])
#'user/my-rules

user> (convert my-rules [:hello :guys "i am" 30 [:congratulate :me]])
;;=> ("HELLO" "GUYS" "i am" 31 ("CONGRATULATE" "ME"))

при таком подходе ваши правила будут выглядеть примерно так:

(def rules
  [[(every-pred coll? (comp #{'not} first)) (fn [data] (map (partial convert [[#{'not} (constantly 'nor)]]) data))]
   [(every-pred coll? (comp #{'or} first)) (fn [data] (map (partial convert [[#{'or} (constantly 'nor)]]) data))]
   [(every-pred coll? (comp #{'and} first)) (fn [[_ & t]] (cons 'nor (map #(list 'nor %) t)))]])
#'user/rules

user> (convert rules '(and x y z))
;;=> (nor (nor x) (nor y) (nor z))

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

(defn first-is 
  "returns a function checking that the input is collection and it's head equals to value"
  [value]
  (every-pred coll? (comp #{value} first)))

, преобразовав ваши правила в:

(def rules
  [[(first-is 'not) (fn [data] (map (partial convert [[#{'not} (constantly 'nor)]]) data))]
   [(first-is 'or) (fn [data] (map (partial convert [[#{'or} (constantly 'nor)]]) data))]
   [(first-is 'and) (fn [[_ & t]] (cons 'nor (map #(list 'nor %) t)))]])
#'user/rules

user> (convert rules '(and x y z))
;;=> (nor (nor x) (nor y) (nor z))

, а затем введя заменяющее правило перезаписи:

(defn replacing
  ([new] [(constantly true) (constantly new)])
  ([old new] [#{old} (constantly new)])) 

, ведущий нас к

(def rules
  [[(first-is 'not) (fn [data] (map (partial convert [(replacing 'not 'nor)]) data))]
   [(first-is 'or) (fn [data] (map (partial convert [(replacing 'or 'nor)]) data))]
   [(first-is 'and) (fn [[_ & t]] (cons 'nor (map #(list 'nor %) t)))]])

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

(defn convert-each [rules]
  (fn [data] (map #(convert rules %) data)))

(def rules
  [[(first-is 'not) (convert-each [(replacing 'not 'nor)])]
   [(first-is 'or) (convert-each [(replacing 'or 'nor)])]
   [(first-is 'and) (fn [[_ & t]] (cons 'nor (map #(list 'nor %) t)))]])

user> (convert rules '(or x y z))
;;=> (nor x y z)

user> (convert rules '(and x y z))
;;=> (nor (nor x) (nor y) (nor z))

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

(defn convert-cons [head-rules tail-conversion]
  (fn [[h & t]] (cons (convert head-rules h) (tail-conversion t))))

(defn transforming [transformer]
  [(constantly true) transformer])

(def rules
  [[(first-is 'not) (convert-each [(replacing 'not 'nor)])]
   [(first-is 'or) (convert-each [(replacing 'or 'nor)])]
   [(first-is 'and) (convert-cons [(replacing 'nor)]
                                  (convert-each [(transforming #(list 'nor %))]))]])

user> (convert rules '(and x y z))
;;=> (nor (nor x) (nor y) (nor z))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...