Common Lisp allows any lisp object to serve as a hash table key. But what if you only want to use part of an object as the key. For example, in
(defstruct action
(name nil :type symbol)
(parameters nil :type list)
(time 0 :type fixnum)
(database nil :type hash-table))
the time slot is inappropriate for equalp hashing situations. What is a good strategy for accessing a hash table using a (partial) lisp object as key? One approach might use a key like (list (action-name act1) (action-parameters act1) (action-database act1)), but this seems rather inefficient. Another approach might create a substructure to the action defstruct with just the three appropriate slots, and use that substructure as a key, but this seems somewhat ad-hoc just for the purpose of hash table access. Are there other methods that could work better?
I will use Common Lisp library from here, as for example
cl-custom-hash-table
Then going to your code, first when you create an action like this:
CL-USER> (setq action-1 (make-action
:parameters '(1 2 3)
:time 45))
produce this error:
The value NIL is not of the expected type HASH-TABLE.
[Condition of type TYPE-ERROR]
so you need to change your definition to something like this:
CL-USER> (defstruct action
(name nil :type symbol)
(parameters nil :type list)
(time 0 :type fixnum)
(database (make-hash-table) :type hash-table))
ACTION
CL-USER> (setq action-1 (make-action
:parameters '(1 2 3)
:time 45))
#S(ACTION :NAME NIL :PARAMETERS (1 2 3) :TIME 45 :DATABASE #<HASH-TABLE :TEST EQL size 0/60 #x3020012E708D>)
Then you should define a function for equal time or what ever you need as follow:
accesing to the data
CL-USER> (action-time action-1)
45
create another action
CL-USER> (setq action-2 (make-action
:parameters '(1 2 3)
:time 102))
#S(ACTION :NAME NIL :PARAMETERS (1 2 3) :TIME 102 :DATABASE #<HASH-TABLE :TEST EQL size 0/60 #x3020012FE58D>)
create the function for testing
CL-USER> (defun equal-actions-by-time (a1 a2) (= (action-time a1) (action-time a2)))
EQUAL-ACTIONS-BY-TIME
define the hash-function:
CL-USER> (defun hash-action (a) (action-time a))
HASH-ACTION
create your hash
CL-USER> (define-custom-hash-table-constructor make-action-hash :test equal-actions-by-time :hash-function hash-action)
MAKE-ACTION-HASH
CL-USER> (defparameter *foo-hash* (make-action-hash) "variable for stackoverflow")
*FOO-HASH*
try it:
CL-USER> (setf (gethash action-1 *foo-hash*) 1
(gethash action-2 *foo-hash*) 10)
10
CL-USER> (gethash action-1 *foo-hash*)
1
T
You can avoid using a library if the distribution will work in implementations that support custom TEST/HASH functions natively, if not you can use with-custom-hash-table
In the optimus case you can work as follow:
CL-USER> (defparameter *foo-hash* (make-hash-table :test 'equal-actions-by-time :hash-function 'hash-action))
*FOO-HASH*
CL-USER> (setf (gethash action-1 *foo-hash*) 1
(gethash action-2 *foo-hash*) 10)
10
CL-USER> (gethash action-1 *foo-hash*)
1
T
Related
Suppose that I have some font-spec in a variable my-font-spec. E.g.
(setq my-font-spec (font-spec :family "XYZ"
:height 120
:weight 'normal
:width 'normal))
I want to pass the attributes in this font-spec as the &rest arguments to set-face-attribute. IOW, I want to, in effect, invoke
(set-face-attribute some-face nil
:family "XYZ"
:height 120
:weight 'normal
:width 'normal)
but do so without spelling out the attributes (as I've done above), but rather indirectly, through some function of my-font-spec.
I'm not sure why, but calling (font-face-attributes my-font-spec) doesn't return the value of the :height attribute you specified:
(:family "XYZ" :weight normal :width normal)
If it did, you could just call:
(apply 'set-face-attribute some-face nil (font-face-attributes my-font-spec))
But to make sure to get the values of all the specified attributes, you can instead get a full list of attributes using the face-attribute-name-alist variable, retrieve the value of each (if present) from my-font-spec, and then apply them to some-face:
(let (props)
(mapcar #'(lambda (attrval)
(let* ((attr (car attrval))
(prop (font-get my-font-spec attr)))
(if prop
(progn (push prop props)
(push attr props))))) face-attribute-name-alist)
(apply 'set-face-attribute some-face nil props))
class Foo
attr_accessor :a,
:time, # ms since epoch
:b,
:c
end
In text mode, the variables listed after 'a' would indent as written above, but in ruby mode they would instead be flush with 'attr_accessor'. How can I get ruby mode to indent like text mode in this situation? Note that I'd like to be able to select the whole file and hit c-m-\ to get the above indentation in addition to all the other ruby-mode.el indentation rules.
This hack should work in the majority of cases.
(defadvice ruby-indent-line (after line-up-args activate)
(let (indent prev-indent arg-indent)
(save-excursion
(back-to-indentation)
(when (zerop (car (syntax-ppss)))
(setq indent (current-column))
(skip-chars-backward " \t\n")
(when (eq ?, (char-before))
(ruby-backward-sexp)
(back-to-indentation)
(setq prev-indent (current-column))
(skip-syntax-forward "w_.")
(skip-chars-forward " ")
(setq arg-indent (current-column)))))
(when prev-indent
(let ((offset (- (current-column) indent)))
(cond ((< indent prev-indent)
(indent-line-to prev-indent))
((= indent prev-indent)
(indent-line-to arg-indent)))
(when (> offset 0) (forward-char offset))))))
Example:
class Comment < ActiveRecord::Base
after_create :send_email_to_author,
:if => :author_wants_emails?,
:unless => Proc.new { |comment| comment.post.ignore_comments? }
end
From Remi (in comments):
Note that Emacs will correctly indent class Foo attr_accessor(:a, :time, # ms since epoch :b, :c) end – Rémi Dec 11 '10 at 8:50
You can add parens and have it indent properly -- I'm adding this here because I'm looking for unanswered questions, and this one comes up (incorrectly, since it has been answered in the comments).
When using Emacs 24.4 or newer, your example will be indented like this by default.
What does the syntax, colons preceding variable in Common Lisp, mean? I've seen programs with such, and I'll present some sample code here, out of a large set of functions.
(defun expand (successorf node)
(mapcar (lambda (action-state-cost)
(let ((action (car action-state-cost))
(state (cadr action-state-cost))
(cost (caddr action-state-cost)))
(make-node :state state :parent node
:action action :path-cost (+ (node-path-cost node) cost)
:depth (1+ (node-depth node)))
))
(funcall successorf (node-state node))
))
Keyword Symbols
:foo is a keyword symbol.
interned in and exported from the KEYWORD package
constantly bound to itself
Usage
Keyword symbols are used when one needs the combination of the following properties:
a symbol is the right data structure
symbols with the same name should be unique (by interning them in a package) -> package KEYWORD
different packages are not needed or wanted -> package KEYWORD
writing the symbol should be easy by not needing to quote them -> :foo better than ':foo
the ability to act as a variable with different values is not needed -> :foo evaluates to :foo itself and only to :foo
In Common Lisp generally symbols can be in a package (kind of a namespace).
An unexported symbol bar in a package foo is written as foo::bar. The double colon is between the package name and the symbol name.
An exported symbol then is written as foo:bar. A single colon is used.
If the symbol is available in the current package then is written as bar without the package.
The package KEYWORD
There is a special package called KEYWORD. A symbol bar in that package is simply and always written as :bar.
Examples
These keyword symbols have also these interesting properties: the symbols are automatically exported from the package KEYWORD (so keyword::bar, keyword:bar, ::bar and :bar are all the same symbol) and they evaluate to themselves:
CL-USER 5 > :bar
:BAR
CL-USER 6 > (describe :bar)
:BAR is a SYMBOL
NAME "BAR"
VALUE :BAR
FUNCTION #<unbound function>
PLIST NIL
PACKAGE #<The KEYWORD package, 0/4 internal, 5830/8192 external>
CL-USER 7 > (eq 'keyword::bar ':bar)
T
CL-USER 8 > (eq :bar ':bar) ; quoted or unquoted, each subform evaluates to :bar
T
Usage
Keyword symbols are used for example as names in named arguments:
(defun foo (&key bar) (+ bar 10))
(foo :bar 7)
Typically they are also used in arguments to instance and structure construction.
(defstruct node state parent action)
DEFSTRUCT is a Common Lisp macro and it generates several functions. One of them is a function MAKE-NODE, which can be used as:
(make-node :state 'open
:parent some-parent
:action an-action)
Note: sometimes the data might also be a keyword. For example in above form, the state might be :open and not open:
(make-node :state :open
:parent some-parent
:action an-action)
They're not variables, actually; those are keywords. They're a special kind of efficient token, similar to “atoms” in other languages. It's a convenient, built-in way to pass named (and, almost always, optional) parameters into a function call.
http://www.gigamonkeys.com/book/functions.html describes the syntax of function calls.
class Foo
attr_accessor :a,
:time, # ms since epoch
:b,
:c
end
In text mode, the variables listed after 'a' would indent as written above, but in ruby mode they would instead be flush with 'attr_accessor'. How can I get ruby mode to indent like text mode in this situation? Note that I'd like to be able to select the whole file and hit c-m-\ to get the above indentation in addition to all the other ruby-mode.el indentation rules.
This hack should work in the majority of cases.
(defadvice ruby-indent-line (after line-up-args activate)
(let (indent prev-indent arg-indent)
(save-excursion
(back-to-indentation)
(when (zerop (car (syntax-ppss)))
(setq indent (current-column))
(skip-chars-backward " \t\n")
(when (eq ?, (char-before))
(ruby-backward-sexp)
(back-to-indentation)
(setq prev-indent (current-column))
(skip-syntax-forward "w_.")
(skip-chars-forward " ")
(setq arg-indent (current-column)))))
(when prev-indent
(let ((offset (- (current-column) indent)))
(cond ((< indent prev-indent)
(indent-line-to prev-indent))
((= indent prev-indent)
(indent-line-to arg-indent)))
(when (> offset 0) (forward-char offset))))))
Example:
class Comment < ActiveRecord::Base
after_create :send_email_to_author,
:if => :author_wants_emails?,
:unless => Proc.new { |comment| comment.post.ignore_comments? }
end
From Remi (in comments):
Note that Emacs will correctly indent class Foo attr_accessor(:a, :time, # ms since epoch :b, :c) end – Rémi Dec 11 '10 at 8:50
You can add parens and have it indent properly -- I'm adding this here because I'm looking for unanswered questions, and this one comes up (incorrectly, since it has been answered in the comments).
When using Emacs 24.4 or newer, your example will be indented like this by default.
In this StackOverFlow question, I created an employee database and provided a select-by-first function. How can I write a select-by-first-pattern where the record is returned if any part of the string/symbol matches?
(select-by-first-pattern 'ev); Returns all records containing "ev" (eg. Steve)
Here's the code needed to construct the database: (UPDATED: Included suggested solution)
(require 'cl)
(defvar *emp-db* nil)
(defun add-record (emp) (push emp *emp-db*))
(defstruct employee age first-name last-name sex children)
(add-record (make-employee))
(add-record (make-employee :age 34
:last-name 'farquharson
:first-name 'alice
:sex 'female))
(add-record (make-employee :age 43
:last-name 'jobs
:first-name 'steve
:sex 'male))
(add-record (make-employee :age 53
:last-name 'ballmer
:first-name 'steve
:sex 'male))
(defun select-by-first (first-name)
(remove-if-not
#'(lambda (employee)
(equal (employee-first-name employee) first-name))
*emp-db*))
;; ---------- Answer below ----------
;;
(defun select-by-first-pattern (first-name)
(remove-if-not
#'(lambda (employee)
(if (string-match first-name (symbol-name (employee-first-name employee))) t nil))
*emp-db*))
(print (select-by-first-pattern "al")); Returns alice's record
(print (select-by-first-pattern "ev")); Returns records of the two Steve's
(print (select-by-first-pattern "ee")); Returns nil
If you want to do partial or pattern matches, you really should be using strings. I don't have any experience with Common Lisp proper, but Emacs has a wealth of regex-matching functions.
If you really are stuck with symbols as inputs, you could use symbol-name (in Elisp at least) to get the name of the symbol as a string. Either way, you're going to end up comparing strings, so you might as well use them to begin with.
Use SYMBOL-NAME to convert a symbol to a string, by preserving the original case:
> (symbol-name '|Alice|)
"Alice"
For simple string searches, you can use the SEARCH function:
> (search "li" (symbol-name '|Alice|))
1
> (search "li" (symbol-name '|Dave|))
NIL
For complex pattern matching, probably this library can help.