【问题标题】:Dot notation in scheme方案中的点符号
【发布时间】:2013-11-26 12:10:05
【问题描述】:

我是 Scheme 编程的初学者。我知道Scheme中的点符号是用来表示一对符号的,例如'(a . b)

第一个元素可以是符号,也可以是列表,没关系。但是特别是第二个元素必须是一个符号,如果不是,可能是一个列表,那么我们不能用内置的cons过程创建一个对。

那么可以创建一对 2 列表吗???好吧,我正在考虑一个解决方案是将列表转换为符号,但实际上这些是 2 完全不同的事情 -> 据我所知是不可能的。

这是我写的代码:

(define compare-attrs
  (lambda (attribute1 attribute2)
    (if (or (and (null? attribute1) (null? attribute2)) (and (not (null? attribute1)) (not (null? attribute2))))
        (cons attribute1 attribute2)
        #f)))

其中attribute1和attribute2是2个列表,我的输出是:

attribute1 atrribute2

预期输出:'(attribute1 .attribute2)

请解释一下。提前致谢!!!

编辑:添加使用 compare-attrs 函数

函数 compare-attrs 用来提取描述实体属性的部分和cons它们组成一对,实体定义如下:

(entity e0 (prov:label "entity e0")
(entity e1 (prov:location "London")

这些实体的属性是(prov:label "entity e0")(prov:location "London")。 应用函数 compare-attrs 时,因为这些属性不是null,所以我期望的输出是

`(prov:label "entity e0") . (prov:location "London")`

【问题讨论】:

  • 什么是entityprov:labelprov:location?我假设它们是函数?
  • (entity e0 (prov:label "entity e0")) 是图中的一个元素
  • this answer 可能会回答这个问题,它描述了 Scheme 中的点表示法,以及如何打印对。
  • "那么是否可以创建一对 2 个列表??" cdr 是列表的对是列表,所以是的,但它与 car 是列表的列表没有任何不同。 ((a b c) . (d e f)) 的行为与 ((a b c) d e f) 和 ((a. (b. (c .()))) 完全相同。 ((d . (e .(f. () ))) . ())) 它只是没有按照您想要的方式显示。

标签: scheme


【解决方案1】:

注意:这是从Recursive range in Lisp adds a period? 的答案中蚕食的,这实际上是在问一个不同的问题。但是,如何打印对的解释是相同的。其余的答案是不同的。

你的问题有点误解,但我想我们可以澄清一下。

第一个 [cons 的参数] 可以是一个符号,也可以是一个列表,它 没关系。但特别是第二个元素必须是一个符号,如果 它不是,例如可能是一个列表,那么我们不能创建一对 带有内置的 cons 程序。

这是不正确的。您可以使用任何您喜欢的参数调用cons,并且您将始终返回一个cons 单元格,其carcons 的第一个参数相同,其@987654327 @ 与 cons 的第二个参数相同。也就是说,cons 唯一重要的是它满足方程式

(eq? a (car (cons a b))
(eq? b (cdr (cons a b))

那么可以创建一对 2 列表吗???嗯,我在想 一个解决方案是将列表转换为符号,但实际上这些是 2 完全不同的事情 -> 据我了解是不可能的。

很有可能;如果您有两个列表,例如 list1list2,您可以通过调用 (cons list1 list2) 来创建 carlist1cdrlist2 的对。现在,我认为您遇到的问题是您希望看到 (<value-of-list1> . <value-of-list2>) 作为输出,并且您看到了一些不同的结果。为了解释为什么会这样,我们需要了解列表在 Lisps 中是如何表示的,以及对是如何打印的。

Scheme 中的列表要么是空列表 ()(在某些 Lisps 中也称为 nil),要么是其 car(也称为 first)是列表元素的 cons 单元格,并且其cdr(也称为rest)要么是列表的其余部分(即另一个列表),要么是终止列表的原子。常规终止符是空列表();以() 终止的列表被称为“正确列表”。由任何其他原子终止的列表称为“不适当的列表”。列表(1 2 3 4 5) 包含元素1、2、3、4 和5,并以() 终止。您可以通过

构建它
(cons 1 (cons 2 (cons 3 (cons 4 (cons 5 ())))))

现在,当系统打印一个cons单元格时,一般情况是通过

来打印
(car . cdr)

例如,(cons 1 2) 的结果打印为

(1 . 2)

由于列表是由 cons 单元格构建的,因此您也可以将这种表示法用于列表:

'(1 2 3 4 5) ==
'(1 . (2 . (3 . (4 . (5 . ())))))

不过,这相当笨拙,所以大多数 lisps(据我所知)都有一个打印 cons 单元格的特殊情况:如果 cdr 是一个列表(另一个 cons 单元格或 ()),那么不要' 不打印.,也不打印cdr 的圆括号(否则它会有,因为它是一个列表)。

现在我们可以解释为什么(cons list1 list2) 的结果看起来不像(<value-of-list1> . <value-of-list2>)。如果您使用两个列表调用cons,您确实会得到一对预期的carcdr,但它不会使用. 表示法打印。例如,

(cons '(1 2 3) '(a b c))
;=> ((1 2 3) . (a b c))   ; which is typically *printed* as
;=> ((1 2 3) a b c)

但同样,打印的表示并不重要,只要以下等式成立:

(eq? a (car (cons a b))
(eq? b (cdr (cons a b))

果然:

(car (cons '(1 2 3) '(a b c)))
;=> (1 2 3)

(cdr (cons '(1 2 3) '(a b c)))
;=> (a b c)

在您询问的具体示例中,考虑调用时会发生什么

(cons '(prov:label "entity e0") '(prov:location "London"))

结果,其实是,

((prov:label "entity e0") . (prov:location "London"))

但是,由于打印规则,这是打印

((prov:label "entity e0") prov:location "London")

尽管如此,您仍然可以使用carcdr 来获取这两个属性:

(car '((prov:label "entity e0") prov:location "London"))
;=> (prov:label "entity e0")

(cdr '((prov:label "entity e0") prov:location "London"))
;=> (prov:location "London")

这就是您以后真正需要做的所有事情。

【讨论】:

    【解决方案2】:

    Scheme 或任何其他 LISP 方言中的点表示法用于创建任意两个值的点对。这些值可以是符号、列表或其他任何东西。它的价值实际上并不重要。例如:

    '(author . aaditmshah) => (author . aaditmshah)
    '((a b c) . (d e f))   => ((a b c) d e f)
    

    如您所见,如果您创建了两个列表的点对,那么第一个列表将被添加到第二个列表的头部。这是因为 LISP 中的列表是嵌套的点对:

    '(a . (b . (c . ())))  => '(a b c)
    

    因此,当您编写 '((a b c) . (d e f)) 时,就好像您正在编写以下内容:

    '((a b c) . (d . (e . (f . ())))) => '((a b c) d e f)
    

    这是完全有效的。您仍然可以像往常一样使用carcdr 分别访问这两个列表:

    (car '((a b c) . (d e f))) => (a b c)
    (cdr '((a b c) . (d e f))) => (d e f)
    

    我不确定您的 compare-attrs 函数应该做什么。您的 if 分支返回一个 cons 单元格,而您的 else 分支返回一个布尔值。对我来说,这完全没有意义。函数的返回类型要一致。

    也许您的问题措辞不正确,因为我不确定您的问题是什么。如果您仍有任何疑问,请告诉我。

    【讨论】:

    • 我添加了函数 compare-attrs 的作用,请再看一下。这些信息我基本了解,但感谢您的解释。
    【解决方案3】:

    我对方案中数据结构的回忆是,您更愿意避免使用虚线对原子(即(a . b)),因为这表示将两个原子组合在一起的结果。如果您查看The Little Schemer,第 3 页,您会看到:


    缺点法则

    原始 cons 有两个参数。 cons 的第二个参数必须是一个列表。 结果是一个列表。


    如果您已阅读其他答案,您就会知道这不是真的。所以老实说,SICP 禁止的 Scheme 数据结构的事情是根据列表定义数据结构布局,然后制作一些小的函数来充当访问器。

    假设你想要一个元组——你可以让它看起来像(a b),这是完全合理的。然后你可以编写这些函数:

    (define (make-tuple a b) (list a b))
    
    (define (tuple-first tup) (car tup))
    (define (tuple-second tup) (car (cdr tup)))
    (define (tuple? tup) (and (list? tup) (eq? 2 (length tup))))
    

    现在它给了我访问器函数和构造函数,我是金子。但这并不是说这是唯一的方法。你也可以直接使用pairs:

    (define (make-tuple a b) (cons a b))
    
    (define (tuple-first tup) (car tup))
    (define (tuple-second tup) (cdr tup))
    (define (tuple? tup) (pair? tup))
    

    所以在你的代码中,你可以使用这里的构造函数来制作你想要的元组。

    一般来说,您的 compare-attrs 函数很奇怪,因为名称并不能真正让我们了解您正在尝试做什么。我更倾向于这样写:

    (define (compare-attrs a1 a2)
        (or
            (and (null? a1) (null? a2))
            (and (not (null? a1)) (not (null? a2)))
        ))
    
    (define (join-populated-attrs a1 a2)
        (if (compare-attrs a1 a2) (make-tuple a1 a2) '()))
    

    您接受两个空属性仍然感觉很有趣,但这可能是您所有问题域的一部分。

    我还应该说,如果您希望输出以特定方式出现,您可能也应该写print-tuple

    【讨论】:

    • (eq? (2 (length tup))) 是一个错误,因为2 不是一个函数。您还可以简化其中一些函数别名;例如,(define tuple-first car) (define tuple-second cdr)。也就是说,我更喜欢这个答案,因为它解决了内涵数据类型的概念,其中数据类型的 internal 表示与其接口分离。
    • 对语法持保留态度,随时更正它并使其变得更好。重要的部分与其说是语法,不如说是责任委托的概念。
    猜你喜欢
    • 1970-01-01
    • 2018-11-01
    • 1970-01-01
    • 2023-03-22
    • 2021-04-24
    • 2015-08-25
    • 1970-01-01
    • 1970-01-01
    • 2018-07-19
    相关资源
    最近更新 更多