【问题标题】:Defmethod on Arbitrary Type Specifiers?任意类型说明符的定义方法?
【发布时间】:2015-01-21 00:29:27
【问题描述】:

我想要做的是:

(defgeneric fn (x))

(defmethod fn ((x (integer 1 *)))
    "Positive integer")

(defmethod fn ((x (integer * -1)))
    "Negative integer")

我想要一个与任意类型说明符一起使用的通用函数,包括基于列表的类型说明符,例如(and x y)(or x y)(satisfies p) 等。现在当我尝试运行上述代码时,我得到一个“无效的专业化程序”错误。一些研究表明defgeneric 旨在与 CLOS 一起使用,而不是与任意类型说明符一起使用。 Common Lisp 中是否有一个类似于 defgeneric 的系统,可以让我获得我想要的任意类型说明符的行为,而不仅仅是类?

【问题讨论】:

  • 零是正负?
  • @RainerJoswig 已修复,但并没有真正改变问题的目的。
  • true,但它暴露了这样一个特性的问题。如果两种方法都匹配:使用哪一种?无法对方法进行排序...对于零,两种方法都是匹配的。应该使用哪一个?
  • @RainerJoswig 是的,但这不是什么大问题。例如 last 或 first 指定的 on 可以被执行。
  • @Coredump:这些后果无处不在。需要对整个类型系统进行排序。它将彻底改变系统的性质。它与零无关。与对象匹配的任何两种类型都需要具有可比性,否则行为将是随机的。

标签: types lisp common-lisp


【解决方案1】:

Common Lisp 定义了两个相关但不相同的层次结构:类型层次结构和类层次结构。每个类都是一种类型,但反之则不然——有些类型不是类。例如,integerstring 是类,因此也是类型。另一方面,(integer 1 *)(satisfies evenp) 是类型,而不是类。

> (type-of "toto")
(SIMPLE-BASE-STRING 4)
> (class-of "toto")
#<BUILT-IN-CLASS STRING>

参数专家——你在defmethod中的参数之后的东西——只能是类名(或(eql value)的形式)。由于(integer 1 *) 不是类名,Common Lisp 不允许您的代码。这样做有一个很好的理由:编译器总是能够确定类层次结构,而类型语言太强大了:

(defun satisfies-the-collatz-conjecture (n)
  (cond
    ((<= n 1) t)
    ((evenp n) (satisfies-the-collatz-conjecture (/ n 2)))
    (t (satisfies-the-collatz-conjecture (+ 1 (* n 3))))))

(subtypep 'integer '(satisfies satisfies-the-collatz-conjecture))
NIL ;
NIL

如果你真的需要你的代码是模块化的,你需要首先将你的值分类成可以做成一个专业的东西,然后分发:

(defmethod fn-generic (x (sign (eql 'positive)))
  "Positive integer")

(defmethod fn-generic (x (sign (eql 'negative)))
  "Negative integer")

(defun classify (x)
  (cond
    ((< x 0) 'negative)
    ((= x 0) 'null)
    ((> x 0) 'positive)))

(defun fn (x)
  (fn-generic x (classify x)))

【讨论】:

    【解决方案2】:

    没有什么 CLOS 可以提供这样的功能。

    它实际上也不适合 CLOS。想想下面,我们有下面的泛型函数调用:

    (generic-function-foo 2)
    

    现在我们为以下类型定义了方法:

    (integer 0 9)
    (integer 1 9)
    (integer 0 99)
    (integer 1 99)
    (integer -1000 1000)
    (or (satisfies evenp) (integer 0 30))
    (satisfies evenp)
    (satisfies divisible-by-two)
    (satisfies all-numbers-which-are-in-my-list-of-numbers)
    

    哪些方法都匹配 2 应该运行?如果我打电话给CALL-NEXT-METHOD,下一个会是哪一个?

    现在我们可以说在源中按出现顺序排列它们。但在 Common Lisp 中,您可以在运行时添加、删除或重新定义方法。这种行为或多或少是随机的。

    我们需要一些其他的冲突解决方案。例如:

    • 手动声明优先级
    • 某种优先级值
    • 运行时错误,用户有机会选择一个
    • 一种类型语言,提供顺序
    • 一起放弃秩序

    已经尝试为 CLOS 提供更具表现力的调度。不过,我不知道向 CLOS 添加类型。请参阅谓词调度过滤调度

    除此之外,我会寻找基于规则的系统,但这通常与 CLOS(Common Lisp 对象系统)非常不同,除非它以某种方式集成。

    【讨论】:

      【解决方案3】:

      您实际上似乎在寻找的是模式匹配,就像在 ML 或 Erlang 中一样。这是一个与 dispatch 完全不同的概念,尽管它们的目的相似。

      一个流行的 Common Lisp 模式匹配库是 optima(可从 Quicklisp 获得)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-01-14
        • 2021-12-27
        • 2022-01-17
        • 2015-02-27
        • 2013-09-21
        • 1970-01-01
        • 2019-05-07
        相关资源
        最近更新 更多