4

我是使用 scribble 的新手,但我不知道如何在我自己的程序中使用它的语法,而不是使用 scribble 语言。

> (define ht (make-hash '(("Name" . "Simon"))))
> (define template "Hello @Name")
> (function-i-dont-know ht template)
"Hello Simon"

我正在寻找的功能是什么?它必须存在,但我在文档中找不到它。

4

3 回答 3

4

添加at-exp以在您选择的语言中使用@-表达式。

#lang at-exp racket

(define x (random 5))
(define y (random 5))

@~a{@x + @y = @(+ x y)}

输出:“3 + 1 = 4”

于 2015-05-13T07:24:26.220 回答
3

@soegaard 给出的答案确实足够完整,但为了那些只寻找更常见的模板系统的人,这里有一种方法可以做到这一点。

主要的是要记住@-forms 只是编写 Racket 代码的另一种方式,所以我们真的在寻找一种基于给定哈希表替换名称的通用方式。(因为 Racket 有很多方法可以做到这一点,所以有很多方法可以用 @-forms 做到这一点。)

这一个使用查找函数,该函数L在保存在参数表中的哈希表中查找值。由于此参数仅在呈现文本时“实时”,它实际上会产生 thunk 以延迟查找直到呈现文本。(我稍微修改了哈希表以保存符号以获得更方便的键。)它使用output函数 from scribble/text产生结果,允许模板中的多种值(如嵌套列表)。出于同样的原因,没有必要尝试使用字符串作为结果,它只是一个事物列表。然后,with-output-to-string用于将文本收集成字符串。

#lang at-exp racket
(require scribble/text)

(define current-replacements (make-parameter #f))
(define (L key) (λ() (hash-ref (current-replacements) key)))
(define (render-with-hash ht template)
  (parameterize ([current-replacements ht])
    (with-output-to-string (λ() (output template)))))

(define ht (make-hash '([Name . "Simon"])))
(define template @list{Hello @L['Name]})
(render-with-hash ht template) ; => "Hello Simon"

一个稍微更方便的变体是使用一个宏来L使引用变得多余:

...
(define-syntax-rule (L key) (λ() (hash-ref (current-replacements) 'key)))
...
(define template @list{Hello @L[Name]})
...

...或者,由于{}s 只是字符串的 @-syntax,返回使用 string 作为哈希键:

#lang at-exp racket
(require scribble/text)

(define current-replacements (make-parameter #f))
(define (L key) (λ() (hash-ref (current-replacements) key)))
(define (render-with-hash ht template)
  (parameterize ([current-replacements ht])
    (with-output-to-string (λ() (output template)))))

(define ht (make-hash '(["Name" . "Simon"])))
(define template @list{Hello @L{Name}})
(render-with-hash ht template) ; => "Hello Simon"

这里要记住的一个警告是{}s 可以是几个字符串,例如,如果文本内容中有换行符。如果你想处理这个问题,你可以调整L函数以接受多个参数并将它们附加在一起,然后在查找完成之前对空格进行规范化:

#lang at-exp racket
(require scribble/text)

(define current-replacements (make-parameter #f))
(define (L . keys)
  (λ() (hash-ref (current-replacements)
                 (regexp-replace #px"\\s+" (string-append* keys) " "))))
(define (render-with-hash ht template)
  (parameterize ([current-replacements ht])
    (with-output-to-string (λ() (output template)))))

(define ht (make-hash '(["First Name" . "Simon"])))
(define template @list{Hello @L{First
                       Name}})
(render-with-hash ht template) ; => "Hello Simon"

在所有这些中有点尴尬的一件事是使用保存哈希表的参数。仅当您不知道预先使用的密钥时才需要这样的东西。在大多数情况下,您可以这样做,为此您可以使用普通变量作为模板的参数,从而成为一个简单的函数:

#lang at-exp racket
(require scribble/text)

(define (template Name)
  @list{Hello @Name})
(with-output-to-string (λ() (output (template "Simon"))))
; => "Hello Simon"

最后一点:我output在所有这些东西中都使用了,所以你可以在文本中拥有嵌套结构。如果您只需要一堆字符串,您可以使用string-append

#lang at-exp racket
(define (template Name)
  @string-append{Hello @Name})
(template "Simon") ; => "Hello Simon"

或者,就像@soegaard 的回答一样,使用该~a函数,它是一种廉价版本的output(到字符串中),可以附加一堆字符串值(和display非字符串值):

#lang at-exp racket
(define (template Name)
  @~a{Hello @Name})
(template "Simon") ; => "Hello Simon"
于 2015-05-15T02:31:50.130 回答
1

首先,要明白 Scribble 只不过是 Racket 代码的前端。Scribble 所做的只是接受一些输入和输出可执行的 Racket 代码

这意味着在你的template字符串上使用 Scribble 阅读器只会给你这个:

"Hello" Name

把它想象成普通的 Racket 代码。它只不过是一个字符串文字"Hello",后跟一个对名为 . 的变量的引用Name。然后将该结果传递给 Racket 编译器并编译成可执行代码。

再说一遍,Scribble 不是模板引擎,它是一种编程语言。没有您描述的“替代”概念,因为 Scribble 只是盲目地吐出代码。您需要运行此代码才能执行任何类型的字符串替换。


您实际上可以通过使用该模块在 Racket 中执行上述操作,该racket/sandbox模块允许您创建自包含的沙盒评估器。

(require racket/sandbox
         scribble/reader)

(define (scribble-eval-string input-str environment)
  (define eval (make-evaluator 'racket))
  (define input (read-inside (open-input-string input-str)))
  (for ([(k v) (in-hash environment)])
    (eval `(define ,(string->symbol k) ,v)))
  (string-append*
   (for/list ([expr (in-list input)])
     (eval `(#%expression ,expr)))))

这个函数做了四件事。racket首先,它为语言创建了一个干净的评估器。接下来,它使用read-insidefromscribble/reader读取输入,它以字符串输入模式读取输入并生成一个列表。根据您的输入,产生的值将是'("Hello " Name)

接下来,它需要将哈希表中的变量注入沙箱的环境中。这是通过手动评估define哈希表中每个键/值对的一组表单来完成的。最后,它将输入列表的每个元素作为表达式求值,然后将结果连接成单个字符串。

有了这一切,你可以这样做:

(define environment (make-hash '(("Name" . "Simon"))))
(define input "Hello @Name")

> (scribble-eval-string input environment)
"Hello Simon"

这是一个好主意吗?可能不是。由于 Scribble 是一种编程语言,因此您可以有效地即时编译整个程序,然后执行它。如果任何数据来自用户,那么您在程序中引入了一个巨大的安全漏洞。

如果您只需要替换哑字符串,只需使用format或类似的东西。但是,如果您真的需要 Scribble 的全部功能,您可以执行以下操作以使其可用。

于 2015-05-13T07:22:28.823 回答