Common Lisp: Generic Function Specializing on Array Length

I am just getting started with generic functions and am wondering if this is possible (I really hope so!).

I have made 3 packages for handling vectors of different lengths: vector2, vector3 and vector4.

Each package has functions that handle vectors of that length:

vector2:normalize - for normalizing *vector2s*
vector3:normalize - for normalizing *vector3s*

My vectors are typed arrays (for speed and memory use as this is for writing games) so a vector3 is:

(make-array 3 :element-type `single-float).

Now I am writing a package called vectors which will contain generic functions to handle any vector types.

So passing vector:normalize a vector3 should return a vector3 and so on.

I tried this:

(defmethod v+1 ((vec-a #.(class-of (make-array 3 
        (vec-b #.(class-of (make-array 3 
  (v3:v+1 vec-a vec-b))

(defmethod v+1 ((vec-a #.(class-of (make-array 4
        (vec-b #.(class-of (make-array 4 
  (v4:v+1 vec-a vec-b))

...based on what I saw in question 6083238, but obviously that only specialized on simple, single-float arrays as:

V> (class-of (make-array 4 :element-type  `single-float))

What would be the best method of doing this, considering it needs to be fast and not memory hogging?

Cheers in advance!


Generic functions in CL can be specialized either with classes or EQL-specializer (see PCL chapter on GFs). Classes aren't types, although there's some relation. But in your case you have a single class and a single type. So, effectively, you want to specialize the methods on some arbitrary property. This can only be achieved with an EQL-specializer:

(defmethod v+1 ((size (eql 3)) vec-a vec-b)
  (v3:v+1 vec-a vec-b))
(defmethod v+1 ((size (eql 4)) vec-a vec-b) 
  (v4:v+1 vec-a vec-b))

They don't do any bounds checking, and also are somewhat more clumsy. The first problem can be solved by adding a check inside the method's body:

(defmethod v+1 ((size (eql 3)) vec-a vec-b)
  (assert (= 3 (length vec-a) (length vec-b))
    "Vector size mismtach")
  (v3:v+1 vec-a vec-b))

You can also define a macro to generate such methods for any size.

Another option is to use a macro at call site, that will present a simpler interface and can perform error checking as well:

(defmacro v+1* (vec-a vec-b)
  (once-only (vec-a vec-b)
    `(if (= (length ,vec-a) (length ,vec-b))
         (v+1 (length ,vec-a) ,vec-a ,vec-b)
         (error "Vector size mismatch"))))

For a discussion of ONCE-ONLY see PCL chapter on macros.

Essentially, there is no way to dispatch based on the size of a vector. As Vsevolod points out, generic functions in CLOS dispatch based on class, and the class of an array in Common Lisp is not altered by the number of elements it holds.

However, if performance is your main aim, then it might not be something you'd want to do either way; involving multiple dispatch in every operation at such a low level could potentially bog you down a bit.

Possible alternatives:

  1. Think like an engineer. Convince yourself that operators on 2-, 3- and 4-vectors are fundamentally different things for your purposes and likely to appear in such different circumstances that it makes sense to have a distict notation for each, then tune those functions as much as possible. Eg: Just define and use +vector3, normalize-vector3 etc.

  2. Think like a mathematician. Define the most general operators possible, ones that should work on any vector length. Worry about performance later, optimizing only those specific parts of the code that slow you down the most in the actual running program. Eg:

    (defun v+ (&rest vectors)
      (apply #'map 'vector #'+ vectors))
    (defun normalize (vector)
      (sqrt (reduce (lambda (acc x) (+ acc (* x x))) vector
                    :initial-value 0)))


  3. Think like people think Common Lisp programmers think and macro it all away. If you want efficiency but feel you need a consistant interface, then if generic functions can't do what you want, or can't do it fast enough, you might try something like this:

    (defvar *vector-op-table* '())   
    (defmacro defvectorops (dimensions &body mappings)   
        `(setf (getf *vector-op-table* ,dimensions) ',mappings))   
    (defun vector-op-reader (stream subchar numarg)   
      (declare (ignore subchar))   
      (let ((form (read stream))   
            (op-table (getf *vector-op-table* numarg)))   
        (sublis op-table form))) 
      #\# #\v #'vector-op-reader)

    Which would allow you to define mappings between the names in your standard vector interface (v+1, normalize etc.) and the names of any specialised functions for performing the associated operations (either named as in suggestion 1, or package-qualified). Eg:

    (defvectorops 2
      (v+1 . +vector2)                ; or vector2::v+1, if you want
      (normalize . normalize-vector2)
    (defvectorops 3
      (v+1 . +vector3)

    Which causes forms such as

    #2v(normalize (v+1 a b)) ; => (normalize-vector2 (+vector2 a b))
    #3v(normalize (v+1 a b)) ; => (normalize-vector3 (+vector3 a b))

    to read as a form using the specialized ops, letting you can define such mappings for any dimension of vector, changing only the digit in #v if you want any peice of code to work for different dimensions of vector.

    (You could have DEFVECTOROPS define these mappings for you if you use a standard naming convention, but sometimes it's best to keep things explicit).

Do bear in mind that any code above is untested (I'm at work and don't have a lisp system available), and the last solution especially is full of Starship Troopers level bugs that will seriously hurt you (there are saner ways to do it, but this was just for illustration), but I just thought it might be good to consider some possible alternate solutions. Which you choose depends on the best fit for the program / programmer. (I'd probably go for option 1 or 2, though.)

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

  • 在普通的 lisp 中,如何以可移植的方式检查对象的类型(In common lisp, how can I check the type of an object in a portable way)
    问题 我想定义一个方法,该方法将专门用于具有无符号字节 8 元素的数组类型对象。 在 sbcl 中,当您(make-array x :element-type '(unsigned-byte 8)) ,对象类由 SB-KERNEL::SIMPLE-ARRAY-UNSIGNED-BYTE-8 实现。 是否有专门针对无符号字节数组类型的实现独立方式? 回答1 使用尖点在读取时插入依赖于实现的对象类: (defmethod foo ((v #.(class-of (make-array 0 :element-type '(unsigned-byte 8))))) :unsigned-byte-8-array) 锐符点读取器宏在读取时评估表单,确定数组的类。 该方法将专门用于特定 Common Lisp 实现用于数组的类。 回答2 请注意, MAKE-ARRAY的:ELEMENT-TYPE参数做了一些特别的事情,它的确切行为可能有点令人惊讶。 通过使用它,您告诉 Common Lisp ARRAY 应该能够存储该元素类型或其某些子类型的项目。 Common Lisp 系统然后将返回一个可以存储这些元素的数组。 它可能是一个专门的数组,也可能是一个也可以存储更通用元素的数组。 注意:它不是类型声明,也不一定会在编译或运行时检查。 UPGRADED-ARRAY-ELEMENT
  • In common lisp, how can I check the type of an object in a portable way
    I want to define a method that will specialize on an object of array type with unsigned byte 8 elements. In sbcl, when you (make-array x :element-type '(unsigned-byte 8)) the object class is implemented by SB-KERNEL::SIMPLE-ARRAY-UNSIGNED-BYTE-8. Is there an implementation independent way of specializing on unsigned-byte array types?
  • 在Common Lisp中查找函数的Arity(Find function's arity in Common Lisp)
    问题 我一直在做一些遗传编程,并且根据他们的特性将函数分为不同的函数集。 这一切都相当复杂。 我想知道是否有更简单的方法可以做到这一点。 例如,如果有一个函数返回给定函数的arity。 提前加油。 回答1 对于解释函数,您应该可以使用function-lambda-expression。 遗憾的是,对于已编译的函数,该函数通常返回nil ,因此您将不得不使用依赖于实现的函数(clocc / port / sys.lisp): (defun arglist (fn) "Return the signature of the function." #+allegro (excl:arglist fn) #+clisp (sys::arglist fn) #+(or cmu scl) (let ((f (coerce fn 'function))) (typecase f (STANDARD-GENERIC-FUNCTION (pcl:generic-function-lambda-list f)) (EVAL:INTERPRETED-FUNCTION (eval:interpreted-function-arglist f)) (FUNCTION (values (read-from-string (kernel:%function-arglist f)))))) #
  • 在 Common Lisp 中,如何定义通用数据类型说明符(如整数列表)?(In Common Lisp, how to define a generic data type specifier (like list of integers)?)
    问题 我想定义一个描述相同类型事物列表的类型说明符。 所以我想要(list-of integer)类似于(array integer) (内置)。 我能够为特定类型创建它,如下所示: (defun elements-are-integer (seq) (every #'(lambda (x) (typep x 'integer)) seq)) (deftype list-of-integer () '(and list (satisfies elements-are-integer))) 但是,这意味着我必须为每种可能的类型执行此操作。 如何更改此代码,以便该类型将另一种类型作为参数,并动态构造satisfies谓词? 问题是, satisfies需要一个全局符号,我不知道如何在适当的上下文中定义谓词函数(我想我需要以某种方式对其进行gensym ,但是如何?)。 此外,该解决方案应该有效,以便可以在另一个包中创建该类型。 回答1 试试这个: (defun elements-are-of-type (seq type) (every #'(lambda (x) (typep x type)) seq)) (deftype list-of-type (type) (let ((predicate (gensym))) (setf (symbol-function predicate)
  • 在Common Lisp中定义setf-expanders(defining setf-expanders in Common Lisp)
    问题 事情是这样的:我没有“获得” setf扩展器,而是想学习它们的工作原理。 我需要学习它们的工作原理,因为我遇到了一个问题,这似乎是为什么您应该学习setf-expanders的典型示例,问题如下: (defparameter some-array (make-array 10)) (defun arr-index (index-string) (aref some-array (parse-integer index-string)) (setf (arr-index "2") 7) ;; Error: undefined function (setf arr-index) 如何为ARR-INDEX编写适当的setf-expander? 回答1 (defun (setf arr-index) (new-value index-string) (setf (aref some-array (parse-integer index-string)) new-value)) 在Common Lisp中,函数名称不仅可以是一个符号,而且可以是两个符号的列表,其中SETF作为第一个符号。 看上面。 因此DEFUN可以定义SETF函数。 该函数的名称为(setf arr-index) 。 setf函数可以以场所形式使用:CLHS:其他复合形式作为场所。 新值是第一个参数。 CL-USER
  • In Common Lisp, how to define a generic data type specifier (like list of integers)?
    I would like to define a type specifier that describes a list of things of the same type. So I would like to have (list-of integer) similar to (array integer) (which is built-in). I am able to create it for a specific type, like this: (defun elements-are-integer (seq) (every #'(lambda (x) (typep x 'integer)) seq)) (deftype list-of-integer () '(and list (satisfies elements-are-integer))) However, this means I have to do this for every possible type. How can I change this code so that the type would take another type as an argument, and construct the satisfies predicate on the fly? The problem
  • Common Lisp 中的类型类(Typeclasses in Common Lisp)
    问题 我想知道是否有办法在 Common Lisp 中模拟 Haskell 的typeclasses 。 泛型函数允许overloading ,并且可以使用deftype定义类型(例如,可以通过某些实例列表的成员资格来定义)。 但我不能dispatch一个类型。 有没有办法让一个类在定义后成为其他类的子类(和子类型)(例如,使cons类成为sequence类的子类,而不重新定义cons )? 谢谢。 回答1 Haskell 中的类型类是一种以字典的形式静态查找“接口”实现的方法(类似于如何使用例如 C++ 中的 vtable,但(几乎)完全是静态的,不像 C++ 在运行时进行动态调度)。 然而,Common Lisp 是一种动态类型语言,因此这样的查找毫无意义。 但是,您可以在运行时实现自己对“类型类”实现(实例)的查找——在像 Common Lisp 这样富有表现力的语言中,这种设计并不难想象。 如果您想参考动态设置中的现有解决方案,PS Python 的 Zope 具有非常相似的特性的适应机制。 回答2 您无法以您设想的方式修改类层次结构,但您可以获得几乎相同的效果。 假设您对sequence的定义是它具有函数sequence-length 。 (defclass sequence ...) (defmethod sequence-length ((s sequence))
  • Common Lisp:将对象传递给方法(Common Lisp: Passing an object to a method)
    问题 我对对象(类实例)的行为有疑问。 代码示例: (defclass game-cards () ((card-symbol :initarg :card-symbol :accessor card-symbol) (colour :initarg :colour :accessor colour))) (defvar *king-hearts* (make-instance 'game-cards :card-symbol 'King :colour 'hearts)) (defvar *ace-spades* (make-instance 'game-cards :card-symbol 'Ace :colour 'spades)) (defclass game-states () ((my-cards :initarg :my-cards :accessor my-cards) (other-cards :initarg :other-cards :accessor other-cards))) (defparameter *state-1* (make-instance 'game-states :my-cards '(*king-hearts* *ace-spades*) :other-cards ())) (defmethod play-game ((state
  • Common Lisp: Passing an object to a method
    I have a problem with the behaviour of objects (class instances). A code sample: (defclass game-cards () ((card-symbol :initarg :card-symbol :accessor card-symbol) (colour :initarg :colour :accessor colour))) (defvar *king-hearts* (make-instance 'game-cards :card-symbol 'King :colour 'hearts)) (defvar *ace-spades* (make-instance 'game-cards :card-symbol 'Ace :colour 'spades)) (defclass game-states () ((my-cards :initarg :my-cards :accessor my-cards) (other-cards :initarg :other-cards :accessor other-cards))) (defparameter *state-1* (make-instance 'game-states :my-cards '(*king-hearts* *ace
  • Lisp:CHAR 既未声明也未绑定(Lisp: CHAR is neither declared nor bound)
    问题 几天前我决定学习 (Common) Lisp,我意识到这是一个相当新手的问题,对于至少有一点经验的人来说,这可能是非常微不足道的。 所以基本上发生的事情是我加载 Emacs + Slime(通过 Lisp in a Box)并编写我的程序(包括在下面): (defun last-char (s) "Get last character" (char s (- (length s) 1))) 然后我尝试用C - c M - k编译它,但是我收到以下警告: CHAR 既未声明也未绑定,它将被视为已声明为 SPECIAL。 这个警告是什么意思? 我想这可能类似于忘记 C 中的 #includes ,但我无法弄清楚。 我该怎么办? 我可以简单地忽略它吗? 回答1 警告意味着char没有被识别为函数,因为它应该,出于某种原因(它报告符号未绑定,它没有价值)。 它可能与您的实现有关。 我已经在我的 SBCL + Emacs/Slime(和 Clozure)中使用 Cc Mk 运行了你的代码,我从 SBCL 的编译中得到了以下报告: ; in: DEFUN LAST-CHAR ; (CHAR S (- (LENGTH S) 1)) ; --> AREF ; ==> ; (SB-KERNEL:HAIRY-DATA-VECTOR-REF ARRAY SB-INT:INDEX) ; ; note
  • 在 Common Lisp 中替换列表中的项目?(Replace an item in a list in Common Lisp?)
    问题 我有一个清单(我将其称为 L)、一个索引(N)和一个新事物(NEW)。 如果我想用 NEW 替换 L at N 中的东西,最好的方法是什么? 我是否应该将子列表从 N 到 N 到列表的末尾,然后使用列表将第一部分 NEW 和最后一部分的新列表粘合在一起? 或者有没有更好的方法来做到这一点? 回答1 (setf (nth N L) NEW) 应该做的伎俩。 回答2 你打算多久做一次; 如果你真的想要一个数组,你应该使用一个数组。 否则,是的,一个由前 N 个元素、新元素和尾部的副本组成的新列表的函数将没问题。 我不知道我头顶上有什么内置函数,但我有一段时间没有用 Lisp 编程了。 这是 Scheme 中的一个解决方案(因为我比 Common Lisp 更了解它,并且有一个解释器来检查我的工作): (define (replace-nth list n elem) (cond ((null? list) ()) ((eq? n 0) (cons elem (cdr list))) (#t (cons (car list) (replace-nth (cdr list) (- n 1) elem))))) 回答3 (setf (nth N L) T) 是最清晰、最简洁、最快捷的方式,如果您想做的是“破坏性”修改,即实际更改现有列表。 它不分配任何新内存。 回答4 我只是尝试修复
  • Problems specializing variable template function
    I am writing a function inListi() which takes at least one argument and compares the first argument to thes list of all subsequent arguments. returns true if first argument == an element in the list, otherwise false. So: if( inListi( 1.2, 2.3, 4.5, 1.2)) std::cout << "Returns true because last argument equals the first argument." << endl; if( inListi( "hello", "world", "HEllo")) std::cout << "This should print out because of the last argument." << endl; Problem is, it doesn't work. I have the code below. For char[N], I copy N portions of the array into a string before continuing. I want to do
  • Common Lisp是否具有类似于Java的Set Interface / implementing类的东西?(Does Common Lisp have a something like java's Set Interface/implementing classes?)
    问题 我需要这样的东西,一个元素集合,其中不包含任何元素的重复项。 Common Lisp,特别是SBCL,是否有这样的东西? 回答1 对于快速解决方案,只需使用哈希表(如前所述)即可。 但是,如果您喜欢更原则的方法,则可以看一下FSet,它是“功能性集合理论集合库”。 除其他外,它包含套件和袋子的类和操作。 (编辑:)最干净的方法可能是将面向集合的操作定义为泛型函数。 毕竟,一组通用函数基本上等效于Java接口。 您可以简单地在标准HASH-TABLE类上实现方法作为第一个原型,并允许其他实现。 回答2 看cl容器。 有一个集合容器类。 回答3 您可以使用列表,尽管它们可能无法有效地表示大型集合。 使用ADJOIN或PUSHNEW将新元素添加到列表中,然后使用DELETE或REMOVE完成相反的操作。 (let ((set (list))) (pushnew 11 set) (pushnew 42 set) (pushnew 11 set) (print set) ; set={42,11} (setq set (delete 42 set)) (print set)) ; set={11} 需要注意的一件事是,这些运算符默认情况下都使用EQL来测试集合中可能存在的重复项(就像Java使用equals方法一样)。 这对于包含数字或字符的集合是可以的,但是对于其他对象的集合
  • 从 Common Lisp 中的文本文件读取数组(Reading an array from a text file in Common Lisp)
    问题 我正在尝试从文本文件中读取 Lisp 中的数据(实际上是一个数组)。 我尝试使用with-open-file和read-line东西,但无法实现我的目标。 我正在寻找的相当于在 MATLAB 中执行data=load('filename.txt') ,这样我就得到一个名为data的数组,它已将整个信息加载到filename.txt 。 文本文件的格式如下 1.0 2.0 3.0 ... 1.5 2.5 3.5 ... 2.0 3.0 4.0 ... ..... 大小也可能不同。 非常感谢。 回答1 这样做的基本方法是使用with-open-file获取输入流,在loop中使用read-line获取行, split-sequence (来自同名库)将其拆分为字段,以及parse-number (来自同名库)将字符串转换为数字。 所有提到的库都可以从 Quicklisp 获得。 编辑:只是为了让您入门,这是一个未经验证的简单版本: (defun load-array-from-file (filename) (with-open-file (in filename :direction :input) (let* ((data-lol (loop :for line := (read-line in nil) :while line :collect (mapcar #
  • Common Lisp中的Python range()模拟(Python's range() analog in Common Lisp)
    问题 如何在Common Lisp中创建连续数字列表? 换句话说,Common Lisp中Python的range函数等效于什么? 在Python中, range(2, 10, 2) 2,10,2 range(2, 10, 2)返回[2, 4, 6, 8] ,其中first和last参数是可选的。 尽管Emacs Lisp具有number-sequence ,但我找不到通用的方法来创建数字number-sequence 。 可以使用循环宏来模拟范围,但我想知道一种可接受的方式来生成带有起点和终点以及步骤的数字序列。 相关:Scheme中Python范围的模拟 回答1 没有生成数字序列的内置方法,典型的方法是执行以下操作之一: 使用loop 编写一个使用loop的实用函数 一个示例实现是(这仅接受从“低”到“高”的计数): (defun range (max &key (min 0) (step 1)) (loop for n from min below max by step collect n)) 这使您可以指定一个(可选)最小值和一个(可选)步进值。 生成奇数:( (range 10 :min 1 :step 2) 回答2 亚历山大实施计划的IOTA: (ql:quickload :alexandria) (alexandria:iota 4 :start 2 :step 2
  • 特化变量模板函数的问题(Problems specializing variable template function)
    问题 我正在编写一个函数 inListi() ,它至少接受一个参数并将第一个参数与所有后续参数的列表进行比较。 如果第一个参数 == 列表中的一个元素,则返回 true,否则返回 false。 所以: if( inListi( 1.2, 2.3, 4.5, 1.2)) std::cout << "Returns true because last argument equals the first argument." << endl; if( inListi( "hello", "world", "HEllo")) std::cout << "This should print out because of the last argument." << endl; 问题是,它不起作用。 我有下面的代码。 对于 char[N],我将数组的 N 部分复制到一个字符串中,然后再继续。 我想这样做,因为我可能会收到一个非空终止的 char[N]。 无论如何,代码如下。 大多数代码是多余的,并且处理 const 和一个参数为 const[N] 而另一个不是该类型的组合。 (顺便问一下,有没有办法减少这种重复?) #include <iostream> #include <stdexcept> #include <string> #include <sstream> #include
  • Lisp 中 C 结构的惯用等价物是什么?(What's the idiomatic equivalent of C structs in Lisp?)
    问题 在 C 类型语言中,从一开始和每本介绍性书籍都非常强调结构/记录和对象。 然后,他们的完整系统是围绕管理这些结构、它们的相互关系和继承而设计的。 在 Lisp 文档中,你通常可以找到 1-2 页关于 Lisp 如何“也”有一个 defstruct,一个简单的例子,通常就是这样。 此外,根本没有提到结构的嵌套。 对于来自 C 背景的人来说,首先似乎分层组织不同的数据类型不是 Lisp 中的首选方法,但除了 CLOS,它是一个成熟的对象系统,如果你只想要结构,那么它太复杂了,除了填充所有内容进入列表,没有明显的方法来转移您的 C 结构知识。 最类似于 C 结构的分层组织数据的惯用 Lisp 方式是什么? —— 我认为我的问题的总结答案是:对于初学者学习目的,defstruct 和/或 plists,虽然“遗留功能”,可以使用,因为它们最接近 C 结构,但它们已被更多的灵活的 defclass/CLOS,这是当今大多数 Lisp 程序所使用的。 这是我关于 SO 的第一个问题,所以感谢大家抽出时间来回答。 回答1 使用 CLOS。 这并不复杂。 否则使用结构。 如果你有一个具体的问题如何使用它们,那就问吧。 (defclass point () ((x :type number) (y :type number))) (defclass rectangle () ((p1
  • Nested `defun` produces a repeated warning in Allegro Common Lisp
    I have a generic implementation of merge sort in Common Lisp: I have different implementation of split and merge functions and, for each combination of a split and merge function I want to construct a merge sort function. Any split function takes a list of strings as input and returns a list of two list: the two halves of the original list. Any merge function takes two sorted lists as input and returns the sorted merged list. Each merge sort function is created by invoking the following function: (defun make-merge-sort-function (split-function merge-function) (defun merge-sort (lst) (if (or
  • 最大方法名称长度(Maximum Method Name Length)
    问题 是否有人碰巧知道您选择的编程语言中方法名称的最大长度是多少? 我打算将其作为C#的特定问题,但是我认为很高兴了解整个领域。 还涉及哪些因素: 语言规范是否对此有所限制? 编译器将其限制在什么范围? 在32位和64位计算机上是否有所不同? 回答1 对于C#,我不认为存在指定的硬性限制。 (例如,C#5规范的2.4.2节没有给出限制。)Roslyn v2.2.0.61624似乎有1024个字符的限制;例如, 这超出了可读性甚至是机器生成的明智名称的范围。 对于Java,规范的3.8节指出: 标识符是Java字母和Java数字的无限长度序列,其中第一个必须是Java字母。 回答2 PHP似乎仅受脚本内存限制的限制。 使用128Mb,我可以创建一个具有400万个字符的类(和方法)。 <?php ini_set('memory_limit', '128M'); $i = 1024 * 1024; while ($i < 10000000) { $className = str_repeat('i', $i); eval("class $className { public function $className() { echo '$i<br>'; } }"); new $className(); $i *= 2; } ?> 回答3 我刚刚在C#Visual Studio 2010
  • 为什么不允许明确指定泛型函数?(Why specializing a generic function explicitly is not allowed?)
    问题 在 Swift 中,应该使用参数类型或返回值来隐式特化泛型函数。 问题是,当我这样调用函数时: func serialize<T>(continuation: GenericWithLongName<T, NSError> -> Void) -> Void 我不能只写 serialize<SomeType> { obj in ... } 它应该是 serialize { (obj: GenericWithLongName<SomeType, NSError>) -> Void in ... } 这看起来很痛苦。 看来这个“功能”已经存在很长时间了。 这是设计决定吗? 允许明确的专业化是否有任何负面影响? 有没有办法让上面的代码整洁干净而不重构那个泛型类? 回答1 “专门化”函数的一种方法是将泛型类型包含为函数参数: func serialize<T>( t: T.Type, continuation: GenericWithLongName<T, NSError> -> Void ) -> Void { } 现在你可以像这样“专门化”这个函数: serialize(SomeType.self) { obj in ... } 我不知道为什么您请求的功能不可用的答案。 我同意您推荐的功能会很有用,但与此同时,它也同样有效,而且几乎同样简洁。