Clojure 请解释一下保罗·格雷厄姆的一些观点;Lisp上的s点

Clojure 请解释一下保罗·格雷厄姆的一些观点;Lisp上的s点,clojure,scheme,lisp,common-lisp,Clojure,Scheme,Lisp,Common Lisp,我需要一些帮助来理解保罗·格雷厄姆的观点 变量的新概念。在Lisp中,所有变量都是有效的指针。值是具有类型的,而不是变量,赋值或绑定变量意味着复制指针,而不是它们指向的对象 符号类型。符号与字符串的不同之处在于,可以通过比较指针来测试相等性 用符号树表示代码的符号 整个语言始终可用。读取时、编译时和运行时之间没有真正的区别。您可以在读取时编译或运行代码,在编译时读取或运行代码,以及在运行时读取或编译代码 这几点是什么意思?它们在C语言或Java语言中有什么不同?除了Lisp家族语言之外,还有其他

我需要一些帮助来理解保罗·格雷厄姆的观点

  • 变量的新概念。在Lisp中,所有变量都是有效的指针。值是具有类型的,而不是变量,赋值或绑定变量意味着复制指针,而不是它们指向的对象

  • 符号类型。符号与字符串的不同之处在于,可以通过比较指针来测试相等性

  • 用符号树表示代码的符号

  • 整个语言始终可用。读取时、编译时和运行时之间没有真正的区别。您可以在读取时编译或运行代码,在编译时读取或运行代码,以及在运行时读取或编译代码

  • 这几点是什么意思?它们在C语言或Java语言中有什么不同?除了Lisp家族语言之外,还有其他语言现在有这些结构吗?

    关于第(1)点和第(2)点,他是在谈论历史。Java的变量几乎相同,这就是为什么需要调用.equals()来比较值的原因

    (3) 是在谈论S表达式。LISP程序是用这种语法编写的,它在java和C等特定语法上提供了很多优点,例如以比C宏或C++模板更干净的方式捕获宏中的重复模式,并用与数据相同的核心列表操作来操作代码。 (4) 以C为例:该语言实际上是两种不同的子语言:if()和while()以及预处理器。您可以使用预处理器来避免一直重复自己,或者使用#if/#ifdef跳过代码。但是这两种语言是完全不同的,并且您不能像在编译时那样使用while()

    C++使用模板会使情况变得更糟。查看一些关于模板元编程的参考资料,它提供了一种在编译时生成代码的方法,对于非专家来说这是非常困难的。此外,使用模板和宏真的是一堆黑客和把戏,编译器无法提供一流的支持-如果你犯了一个简单的语法错误,编译器就无法给你一个清晰的错误消息


    好的,使用Lisp,您可以用一种语言实现所有这些。您在运行时使用与第一天学习相同的东西来生成代码。这并不是说元编程是琐碎的,但在一流的语言和编译器支持下,它肯定更简单。

    马特的解释非常好——他尝试与C和Java进行比较,我不会这么做——但出于某种原因,我真的很喜欢偶尔讨论这个话题,这是我的答案

    关于第(3)和(4)点: 你清单上的第(3)点和第(4)点似乎是最有趣的,而且现在仍然相关

    为了理解它们,清楚地了解Lisp代码在执行过程中会发生什么是非常有用的,Lisp代码以程序员键入的字符流的形式出现。让我们用一个具体的例子:

    ;;一个完整的库导入,
    ;; 我们不关心这件事
    (需要“[clojure.contrib.string:as str]”)
    ;; 这是有趣的一点:
    (println(str/replace re#“\d+”FOO“a123b4c56”))
    
    这段代码打印出
    aFOObFOOcFOO
    。请注意,Clojure可能并不完全满足列表中的第四点,因为读取时间对用户代码并不真正开放;不过,我将讨论这将意味着什么

    所以,假设我们在某个文件中找到了这段代码,我们让Clojure执行它。另外,为了简单起见,让我们假设已经通过了库导入。感兴趣的位开始于
    (println
    ,结束于右侧较远的
    。正如人们所期望的那样,这是经过词法分析/解析的,但已经出现了一个重要的问题:结果不是某些特定于编译器的AST表示——它只是一个常规的Clojure/Lisp数据结构,即一个包含一组符号的嵌套列表,字符串以及(在本例中)与
    #“\d+”
    文本相对应的单个已编译正则表达式模式对象(下文将对此进行详细介绍)。一些口齿不清的人会在这个过程中加入他们自己的小曲折,但是PaulGraham主要是指普通的口齿不清。在与您的问题相关的问题上,Clojure与CL相似

    编译时的整个语言: 在此之后,编译器处理的所有内容(对于Lisp解释器也是如此;Clojure代码总是被编译)都是Lisp程序员用来操作的Lisp数据结构。在这一点上,一个奇妙的可能性变得显而易见:为什么不允许Lisp程序员编写Lisp函数来操作表示Lisp程序的Lisp数据,并输出表示转换程序的转换数据,以替代原始程序?换句话说——为什么不允许Lisp程序员将他们的函数注册为各种编译器插件,在Lisp中称为宏?事实上,任何像样的Lisp系统都有这种能力

    因此,宏是在编译时在程序表示上运行的常规Lisp函数,在发出实际目标代码的最终编译阶段之前。由于宏可以运行的代码种类没有限制(特别是它们运行的代码本身通常是自由使用宏工具编写的),因此可以说“整个语言在编译时可用”

    阅读时的全部语言: 让我们回到
    #“\d+”
    regex-literal。如上所述,在编译器第一次听到要编译的新代码之前,它在读取时被转换为实际的编译模式对象。这是怎么发生的

    Clojure目前的实现方式与Paul Graha的有所不同
    (defun print-twice (it)
      (print it)
      (print it))
    
    (type-of "abc")  -> STRING
    
    |This is a Symbol|
    this-is-also-a-symbol
    
    (find-symbol "SIN")   ->  SIN
    
    (eq 'sin 'cos) -> NIL
    (eq 'sin 'sin) -> T
    
    (defvar *sentence* '(mary called tom to tell him the price of the book))
    
    (count 'the *sentence*) ->  2
    
    (eval '(* 3 (+ 2 5))) -> 21
    
    (length '(* 3 (+ 2 5))) -> 3
    
    CL-USER 8 > (sdraw '(* 3 (+ 2 5)))
    
    [*|*]--->[*|*]--->[*|*]--->NIL
     |        |        |
     v        v        v
     *        3       [*|*]--->[*|*]--->[*|*]--->NIL
                       |        |        |
                       v        v        v
                       +        2        5