小编dav*_*ugh的帖子

密钥的替代哈希表相等性测试

SBCL分析显示我的一个Common Lisp哈希表函数耗费了大量时间.该函数比较两个哈希表以确定它们是否具有相同的键:

(defun same-keys (ht1 ht2)
  "Returns t if two hash tables have the same keys."
  (declare (hash-table ht1 ht2))
  (when (= (hash-table-count ht1) (hash-table-count ht2))
    (maphash (lambda (ht1-key ht1-value)
               (declare (ignore ht1-value))
               (unless (gethash ht1-key ht2)
                 (return-from same-keys nil)))
             ht1)
    t))
Run Code Online (Sandbox Code Playgroud)

有没有办法加速这个,因为哈希表总是#'eqlfixnum键?我也在加载lparallel库,但在这种情况下以某种方式并行化函数是否有意义?

编辑:哈希表的大小可以是大约10到100个条目.ht键范围从100扩展到999,999,999,999,但在此范围内实际使用的总可能的fixnums是稀疏的.每个ht值都是t或列表.所有哈希表的键值关联都在加载时设置.通过复制现有哈希表并逐步添加或删除条目,在运行时创建新哈希表.常规哈希表的读取,写入和复制似乎不是问题.

mapping parallel-processing hashtable common-lisp

4
推荐指数
1
解决办法
55
查看次数

可扩展的矢量类型

如何正确指定可扩展向量的common-lisp类型(即,vector-push-extend可接受),因此可以复制它.例如,如果向量定义为:

(defparameter v (make-array 2
                            :initial-contents '((a (b)) (c (d) e))
                            :adjustable t
                            :fill-pointer t))
Run Code Online (Sandbox Code Playgroud)

我复制它的简单(不正确)方法是:

(map 'array #'copy-tree v)
Run Code Online (Sandbox Code Playgroud)

但这会在sbcl中生成一个类型错误.一个正确的序列类型规范可以使这个工作吗?

vector common-lisp adjustable-array

3
推荐指数
1
解决办法
327
查看次数

从字符串中读取 Lisp 对象

我熟悉从文件中收集 Lisp 对象的基本模板,如下所示:

(with-open-file (stream "filename.lisp")
    (loop for object = (read stream nil 'eof)
          until (eq object 'eof)
          collect object))
Run Code Online (Sandbox Code Playgroud)

但我不确定如何将其转换为从字符串中收集对象,例如使用read-from-string. 那么您是否必须跟踪字符串中停止位置的索引?另外,如何避免与输入中的或eof等任何其他合法 Lisp 对象发生名称冲突?nilt

string common-lisp

3
推荐指数
1
解决办法
1191
查看次数

Common-Lisp中`find`函数的异常行为

在搜索nil序列中的使用find总是返回NIL

(find nil #(a b nil c)) -> NIL

(find nil #(a b c)) -> NIL
Run Code Online (Sandbox Code Playgroud)

如果序列是列表,则相同。

但是,member运行符合我的预期:

(member nil '(a b nil c)) -> (NIL C)
Run Code Online (Sandbox Code Playgroud)

为什么find设计为以这种方式运行?

请注意,它的position工作与我期望的一样:

(position nil #(a b nil c)) -> 2

(position nil #(a b c)) -> NIL
Run Code Online (Sandbox Code Playgroud)

search common-lisp

3
推荐指数
1
解决办法
54
查看次数

在Lparallel库中使用队列(通用Lisp)

lparallel库中队列的基本讨论,网址https://z0ltan.wordpress.com/2016/09/09/basic-concurrency-and-parallelism-in-common-lisp-part-4a-parallelism-using-lparallel- basics /#channels说,该队列“启用在工作线程之间传递消息”。下面的测试使用一个共享队列来协调主线程和从属线程,其中主线程在退出之前只是等待下一个线程的完成:

(defun foo (q)
  (sleep 1)
  (lparallel.queue:pop-queue q))  ;q is now empty

(defun test ()
  (setf lparallel:*kernel* (lparallel:make-kernel 1))
  (let ((c (lparallel:make-channel))
        (q (lparallel.queue:make-queue)))
    (lparallel.queue:push-queue 0 q)
    (lparallel:submit-task c #'foo q)
    (loop do (sleep .2)
             (print (lparallel.queue:peek-queue q))
          when (lparallel.queue:queue-empty-p q)
            do (return)))
  (lparallel:end-kernel :wait t))
Run Code Online (Sandbox Code Playgroud)

这可以按预期的方式产生输出:

* (test)

0
0
0
0
NIL
(#<SB-THREAD:THREAD "lparallel" FINISHED values: NIL {10068F2B03}>)
Run Code Online (Sandbox Code Playgroud)

我的问题是我是否正确或完全使用了lparallel的队列功能。似乎队列只是使用全局变量来保存线程共享对象的替代品。使用队列的设计优势是什么?通常为每个提交的任务分配一个队列(假设任务需要进行通信)是一种好习惯吗?感谢您提供更深入的见解。

multithreading common-lisp message-queue

3
推荐指数
1
解决办法
117
查看次数

如何正确终止正在阻塞的线程(Lparallel Common Lisp)

在Lparallel API中,终止所有线程任务的推荐方法是使用停止内核(lparallel:end-kernel)。但是,当线程正在阻塞时(例如,(pop-queue queue1)等待某个项目出现在队列中),当内核停止时,该线程仍将处于活动状态。在这种情况下(至少在SBCL中),内核关闭偶尔(但并非每次)失败,并显示以下信息:

debugger invoked on a SB-KERNEL:BOUNDING-INDICES-BAD-ERROR in thread
#<THREAD "lparallel" RUNNING {1002F04973}>:
  The bounding indices 1 and NIL are bad for a sequence of length 0.
See also:
  The ANSI Standard, Glossary entry for "bounding index designator"
  The ANSI Standard, writeup for Issue SUBSEQ-OUT-OF-BOUNDS:IS-AN-ERROR

debugger invoked on a SB-SYS:INTERACTIVE-INTERRUPT in thread
#<THREAD "main thread" RUNNING {10012E0613}>:
  Interactive interrupt at #x1001484328.
Run Code Online (Sandbox Code Playgroud)

我假设这与阻塞线程无法正确终止有关。关闭内核之前,应该如何正确终止阻塞线程?(API表示kill-tasks仅应在特殊情况下使用,我认为这种情况不适用于这种“正常”关闭情况。)

multithreading common-lisp blockingqueue

3
推荐指数
1
解决办法
94
查看次数

从序列中删除所有重复元素

Common Lisp序列函数remove-duplicates在每个多重性后面都保留一个元素。以下类似功能的目标remove-equals是删除所有重复项。

但是,我想使用内置函数remove-if(而不是迭代)和SBCL的哈希表功能用于:test函数,以将时间复杂度保持在O(n)。迫在眉睫的问题是SBCL相等性测试需要是全局的,但是该测试还需要依赖于的key参数remove-equals。可以满足两个要求吗?

(defun remove-equals (sequence &key (test #'eql) (start 0) end (key #'identity))
  "Removes all repetitive sequence elements based on equality test."
  #.(defun equality-test (x y)
      (funcall test (funcall key x) (funcall key y)))
  #.(sb-ext:define-hash-table-test equality-test sxhash)
  (let ((ht (make-hash-table :test #'equality-test)))
    (iterate (for elt in-sequence (subseq sequence start end))
             (incf (gethash (funcall key elt) ht 0)))
    (remove-if (lambda (elt)
                 (/= 1 (gethash elt ht)))
               sequence :start …
Run Code Online (Sandbox Code Playgroud)

hashtable sbcl common-lisp sequence

3
推荐指数
2
解决办法
150
查看次数

使用 ASDF 的 :around-compile For Individual Files

包含 coredump 响应的SO 帖子展示了如何将编译器策略应用于 ASDF 系统的组件文件:

(defsystem simple-system
  :serial t
  :around-compile (lambda (next)
                    (proclaim '(optimize (debug 3) 
                                         (safety 3)
                                         (debug 3)
                                         (speed 0)))
                    (funcall next))
  :components ((:module "src"
                        :components
                        (...))))
Run Code Online (Sandbox Code Playgroud)

它还提到您可以“隐藏”单个文件,但这将如何工作。这让我感到困惑,因为next在 lambda 表达式中绑定到一个闭包。由于我只需要对几个组件文件应用优化,你如何将这些文件名赋予:around-compile?

common-lisp asdf compiler-optimization

3
推荐指数
1
解决办法
79
查看次数

Common Lisp对函数的引用

在Common Lisp中引用函数似乎有许多不同的方法:

  1. 通过符号,其中符号出现(未引用)作为形式的汽车(1+ 2) => 3,或在功能参数位置(mapcar '1+ '(1 2 3)) => (2 3 4);

  2. 通过功能对象,其中,所述(解释或编译)函数对象可以出现在函数自变量的位置中(mapcar #'1+ '(1 2 3)) => (2 3 4)(mapcar (symbol-function '1+) '(1 2 3)) => (2 3 4),而不是作为一种形式如在车上(#'1+ 2) => error((symbol-function '1+) 2) => error;

  3. 通过lambda表达式,其中lambda表达式显示为lambda形式的汽车((lambda (x) (1+ x)) 2) => 3,或者在函数参数位置中显示(mapcar (lambda (x) (1+ x)) '(1 2 3)) => (2 3 4)[但是,Hyperspec不会将lambda表达式识别为"函数指示符"].

在这三种"方式"中,对我而言,第一种似乎有些不合适,因为它似乎使Common Lisp运算符仅仅评估其参数一次的基本准则复杂化.如果在上面的示例'1+中进行评估,它将生成符号1+ …

lambda function common-lisp

2
推荐指数
1
解决办法
384
查看次数

如何正确指定可调向量的元素类型

以下是旨在用作(lifo)堆栈或(fifo)队列的结构

(defstruct jvector
  (vector (make-array 0 :adjustable t :fill-pointer 0) :type (array * (*)))
  (start 0 :type (integer 0 *)))
Run Code Online (Sandbox Code Playgroud)

内容的范围从jvector-start到jvector-vector填充指针。我希望能够用类似的东西指定内容的元素类型

(defun create-jvector (&key (element-type t))
  (make-jvector :vector (make-array 0 :element-type element-type :adjustable t :fill-pointer 0)
                :start 0))
Run Code Online (Sandbox Code Playgroud)

并用

(defun push-jvector (elt jvec)
  (vector-push-extend elt (jvector-vector jvec)))
Run Code Online (Sandbox Code Playgroud)

但是,create-jvector中的元素类型将被忽略。例如,

* (defparameter v (create-jvector :element-type 'integer))
V
* v
#S(JVECTOR :VECTOR #() :START 0)
* (push-jvector 1 v)
0          ;OK result
* v
#S(JVECTOR :VECTOR #(1) :START 0)
* (push-jvector 'a v) …
Run Code Online (Sandbox Code Playgroud)

types common-lisp adjustable-array

2
推荐指数
1
解决办法
82
查看次数