Схема для замыкания функции (субст) - PullRequest
0 голосов
/ 06 апреля 2020

Я читаю Корни Лиспа Пола Грэма

Я попытался преобразовать функцию subst на странице 5, которая определяется следующим образом:

(defun subst (x y z)
  (cond ((atom z)
         (cond ((eq z y) x)
               ('t z)))
        ('t (cons (subst x y (car z))
                  (subst x y (cdr z))))))

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

(defn subst
 [x y z]
 (if (not (nil? z)) z x)
     (if (= z y) x z)
    (cons (subst x y (first z))
          (subst (x y (rest z)))))

Ответы [ 3 ]

4 голосов
/ 06 апреля 2020

"Traduttore, traditore"

(Это можно перевести как "переводчик, предатель", но это разрушает каламбур, что само по себе весело)

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

Несмотря на то, что Clojure имеет функции cons и nil?, они не означают одно и то же как в Common Lisp (соотв. cons и null): см. clojure: нет минусов для деталей. Прежде чем вы сможете перевести subst, вы должны определить, что идиоматическое c нужно сделать в Clojure.

Обычно subst используется для преобразования дерева , сделанного из минусы клетки; обратите внимание, например, что subst не переходит в векторы, строки и т. д. c. Среди этих деревьев определенным подмножеством деревьев являются те, которые являются формами Лисп. Фактически, одним из важных вариантов использования subst является поиск и замена форм во время генерации кода.

Если вы ограничитесь типом Clojure Cons, вы не будете поддерживать код как данные, насколько я знаю. Так как в коде Clojure также используются векторы и карты, вам, вероятно, потребуется использовать такие объекты. Итак, как перевести subst не так просто определить.

Возможной отправной точкой является чтение LispReader.java, чтобы определить набор объектов, составляющих AST, и Посмотрите, какой вид кода вы хотите делать.

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

2 голосов
/ 06 апреля 2020

перевод версии схемы может выглядеть следующим образом:

(defn my-subst [new old data]
  (when-not (nil? data)
    (cond (sequential? data) (cons (my-subst new old (first data))
                                   (my-subst new old (next data)))
          (= data old) new
          :else data)))

user> (my-subst 1 :x '(1 2 :x (:x 10 :x [:x :z :x])))
;;=> (1 2 1 (1 10 1 (1 :z 1)))

это довольно близко (хотя и не совсем то же самое, поскольку существует более одного собственного типа коллекции, что заставляет вас столкнуться с выбор: какие из них следует рассматривать как цели для замены). В этом примере обрабатываются «списочные» (последовательные) структуры, в то же время опуская ha sh map и sets. Другая проблема заключается в сохранении формы И типа исходной последовательности, которая на самом деле не так тривиальна, как кажется (например, (into (empty (list 1 2 3)) (list 1 2 3)) => (3 2 1)

Итак, что вам нужно сделать, это сначала решить, семантика подстановки, хотя в схеме это просто естественная обработка списка.

Начиная с clojure.walk, который уже упоминался, самый простой способ использовать его для подстановки может быть

(defn subst [new old data]
  (clojure.walk/prewalk-replace {old new} data))

user> (subst :z :x [1 :x 3 '(:x {:a :x}) #{:x 1}])
;;=> [1 :z 3 (:z {:a :z}) #{1 :z}]
0 голосов
/ 06 апреля 2020

Вот как я бы это сделал, включая юнит-тест для проверки:

(ns tst.demo.core
  (:use tupelo.core tupelo.test)
  (:require [clojure.walk :as walk]))

(defn subst
  [replacement target listy]
  (walk/postwalk (fn [elem]
                   (if (= elem target)
                     replacement
                     elem))
    listy))

(dotest
  (is= (subst :m :b [:a :b [:a :b :c] :d])
    [:a :m [:a :m :c] :d]))

Однако я бы не стал тратить много времени на чтение 40-летних текстов на Common Lisp, даже если бы я думаю, что книга Пола Грэма Хакеры и художники - это просто умопомрачительно.

Clojure развил современное состояние для lisp по крайней мере на один порядок (я бы сказал, более 2). Основные улучшения включают использование JVM, постоянных структур данных, параллелизма, синтаксиса и литералов данных, и это лишь некоторые из них.

Пожалуйста, ознакомьтесь с этим списком учебных материалов Clojure и, возможно, начать с Получение Clojure или аналогичное.


Обновление

Больше от Пол Грэм на Clojure

enter image description here

...