根据条件引用变量

Reference to a variable depending on a conditional

我们可以根据条件选择运算符:

(let ((x 10))
    (display ((if (< 0 1) - +) x))
    (newline)
)
; output -10

我们可以对变量做同样的事情吗?

(let ((x 0) (y 0))
    (set! (if (< 0 1) x y) 1) ; evaluate a reference to a variable, not his value
    (display x) (newline)
    (display y) (newline)
)

如果条件为真,则设置 x 为 1,否则设置 y 为 1。

我的意思是不重复 set! 函数,我知道可以使用以下方法解决它:

(let ((x 0) (y 0))
    (if (< 0 1)
        (set! x 1)
        (set! y 1)
    )
    (display x) (newline)
    (display y) (newline)
)

目的是清理这段代码:

(define-macro (inc x n)
    `(set! ,x (+ ,x ,n))
)
define hmap (make-hash-table 50))
(hashq-set! hmap 'foo "bar")
(hashq-set! hmap 'bar "foo")
(hashq-set! hmap 'baz 42)
(let ((n 0) (n-strings 0) (n-numbers 0))
    (hash-fold
        (lambda (key value seed)
            (inc n 1)
            (if (string? value)
                (inc n-strings 1)
                (if (number? value)
                    (inc n-numbers 1)
                )
            )
        )
        0 hmap
    )
    (format #t "The map contains ~a elements: ~a are strings and ~a are numbers\n"
        n n-strings n-numbers
    )
)

如何更优雅的实现?

可以这样读取变量,因为读取变量的结果只是一个普通的值。但是变量本身不是 first-class:你不能以仍然允许你写入它的方式存储 (if (< 0 1) x y) 的结果。

我认为你可以,但 set! 不会。我不确定下面的宏是否完全正确,但我认为它可能是。不过我只在 Racket 中测试过它。

(define-syntax st!
  (syntax-rules (if cond)
    [(_ (if test a b) v)
     (if test
         (st! a v)
         (st! b v))]
    [(_ (cond
          [test a]
          ...) v)
     (cond
       [test (st! a v)]
       ...)]
    [(_ var val)
     (set! var val)]))

然后

> (let ((x 0) (y 0))
    (st! (if (< 0 1) x y) 1)
    (display x) (newline)
    (display y) (newline))
1
0

请注意,你需要对所有你想让它进入的东西进行特殊处理,而且我对 syntax-rules 中的整个文字内容也有点不确定(我真的是穴居人 CL 人, 所以我只对用泥巴和碎饼干做的宏系统很满意。