使用字符串创建红色语言中的单词和路径

Use strings to create words and paths in Red language

我在 namelist 中有字符串,它们对应于应用程序中的变量和字段名称。

函数应该从namelist中读取字符串,加上一个'f'得到field_names,然后把变量值放在相应的字段中。

我试过下面的代码,没有给出任何错误,但也不起作用:

namelist: ["var1" "var2"]
var1: 5
var2: 10

process: [
    repeat i length? namelist [
        (to-set-path compose rejoin [namelist/:i "f/text"] (to-word namelist/:i))
    ]
]

lay: layout [ 
    text "Values to appear here: "
    var1f: field "a"
    var2f: field "b"

    button "Click" [do process]
]

view lay

一般来说:将字符串转换为 WORD!s (例如 to-word "foo" 很容易。然而,神奇地创造这个词可能很难!引用绑定到 "the variable you meant"。这样做的巧妙原因与 没有范围 这一事实有关。参见:

Is there a overall explanation about definitional scoping in Rebol and Red

因此,无论如何,您尝试做的事情都会有点狡猾。有更好的方法。但是为了避免不问这个问题,我将解释这里发生了什么以及如何以您尝试的方式修复它。

更正后的版本仅用于教学目的。请换一种方式。

compose rejoin [namelist/:i "f/text"]

REJOIN 应用于块,并合并内容,结果类型 loosely 基于第一个元素。 (这是一个 questionable operation,但在 Rebol 代码中历来很流行。)

由于 namelist/:i 是一个字符串,您的 REJOIN 将生成一个字符串...并且该字符串最终将传递给 COMPOSE。但 COMPOSE 旨在应用于 BLOCK!s... 并在其中搜索带括号的组 inside,评估它们,同时保留其余代码。它是一种块模板系统,对其他类型的输入没有影响......所以你会得到相同的字符串。

TO-SET-PATH 因此被输入了一个字符串! (例如 "var1f/text")。我什至不知道路径转换接受字符串。我发现此操作的行为令人费解,因为它显然加载了字符串,然后使其成为长度为 1 SET-PATH!.

的奇异元素
>> p: to-set-path "foo/bar"
== foo/bar: ;-- huh? really, did that work?

>> type? p
== set-path! ;-- ok, good, I guess.

>> length? p
== 1 ;-- wait, what?

>> type? first p
== path! ;-- a PATH! inside a SET-PATH!...?

>> length? first p
== 2

>> type? first first p
== word!

>> foo: 10
>> get first first p
== 10 ;-- well, at least it's bound

那可不是SET-PATH那种!你要;你想要一个 SET-PATH! 2个字!元素。转换块!设置路径!将是这样做的一种方式。

to-set-path compose [(load rejoin [namelist/:i "f"]) text]

现在我们看到 COMPOSE 被正确使用了,它将 运行 括号内的计算并保留 text 单词。这会产生一个包含 2 个元素的块,它很容易转换为 SET-PATH!。我使用 LOAD 而不是 TO-WORD 来处理连接到实际变量的一些 "magic",而普通单词转换不会这样做。但这只是一种变通方法 -- 不确定,并不总是解决问题的方法。

但是生成一个 SET-PATH!并不意味着 运行s。如果我说:

s: to-set-word "x"
probe type? s

没有设置字!被执行,它只是被生成。在这种情况下,存储在变量 s 中。但是如果我没有把它存储在一个变量中,评估产品就会被扔掉……如果我写1 + 1 print "hi",方式2就会被扔掉。要执行 SET-PATH!,您需要将它放在一个上下文中,它将被组合到源代码中并进行评估。

(注意:Ren-C 有一个 primitive called EVAL 可以即时执行此操作,例如 eval (quote x:) 10 会将 10 分配给 x。)

但是在 Red 中你需要做这样的事情:

namelist: ["var1" "var2"]
var1: 5
var2: 10

process: [
    repeat i length? namelist [
        do probe compose [
            (to-set-path compose [(load rejoin [namelist/:i "f"]) text])
            to-string
            (load namelist/:i)
        ]
    ]
]

lay: layout [ 
    text "Values to appear here: "
    var1f: field "a"
    var2f: field "b"

    button "Click" [do process]
]

view lay

现在您的外部 COMPOSE 正在构建一个 3 元素块,其中第一个元素是 SET-PATH!,第二个是 WORD!从字面上看,它只是将您的整数转换为字符串,第三个是 WORD!这将被评估为相关的整数。该块的DO将具有赋值效果。

我把你的 to-word namelist/:i 改成了 load namelist/:i。同样,由于我提到的原因......单独的 TO-WORD 并没有放在 "binding".

我在那里留下了一个 PROBE,这样您就可以看到构建和执行的内容:

[var1f/text: to-string var1]
[var2f/text: to-string var2]

PROBE 是一个非常有用的工具,它输出它的参数,但也传递它。您可以在代码中的不同位置插入它,以便更好地了解正在发生的事情。

(注意:如果你想知道为什么我不建议编写一个仅适用于 SET-PATH 的窄 EVAL-2 辅助操作!,那是因为这样的东西存在于更好的名称。它被称为 SET。尝试 set (quote x:) 10 然后 print x。事实上,这就是你实际想要做的事情的变体...... obj: make object! [a: 10] 然后 set (in obj 'a) 20 然后print obj/a。正如我所说,有很多更好的方法来完成你正在做的事情,但我试图专注于按照你正在尝试的方式去做。)

第 1 步:重构

您的代码已重新格式化并添加了 print (1) 条语句:

namelist: ["var1" "var2"]
var1: 5
var2: 10

process: [
    print "process: start"      ; (1)
    repeat i length? namelist [
        (to-set-path compose rejoin [namelist/:i "f/text"] (to-word namelist/:i))
    ]
    print "process: end"        ; (1)
]

lay: layout [ 
    text "Values to appear here: "
    var1f: field "a"
    var2f: field "b"

    button "Click" [do process]
]

view lay

当我在 控制台 中 运行 并按下 "Click" 时,它给出以下内容:

process: start
process: end

所以我知道至少按钮有效

第 2 步:使用 print

进行调试

现在我可以集中精力,在代码块内移动 print

process: [
    repeat i length? namelist [
        print (
            to-set-path compose rejoin [
                namelist/:i "f/text"
            ] (to-word namelist/:i)
        )
    ]
]

我几乎立刻就能看出这里出了什么问题:

var1    ; expecting `var1f` here
var2    ;

第 3 步:我们需要更深入 probe

放在一边

现在,在我继续之前,请注意这段代码无法访问 视图块内的任何内容(因为它不起作用!)。 但这里的好处是你可以忽略它并稍后再回来。

您需要一种以编程方式访问 var1f/text 的方法

记住这一点,这里有一个更好的方式来表达这个问题:

步骤 3a:如何动态创建具有不同名称的 objects 并为其设置值?

var1f/text: 5

(给定步骤 2 中的代码)

现在,我遇到了一个难题。这可能最好作为一个不同的、更简单的问题来问。

我决定继续假设你完成了这个(还有另一个答案)

备注

在这一步中要带回家的重要事情是 datatype 红色视图 使用和你正在使用的是同一件事:红色 object秒。 没有区别(都是简单人脸对象的实例)

第 4 步:大功告成!或者你是?

这样您就可以为您的工作创建所需的图形用户界面了! 对吗?

但是你问自己,这是最好的方法吗? 如果你想, or 怎么办?

  1. 你有read the official gui docs especially the part about view engine
  2. 您看过 vid and adding view face objects manually
  3. 的示例
  4. 您查看了 github 上的 sample code and small apps
  5. 的回购
  6. 您甚至尝试过旧的但稳定的 rebol2

但是你还是不明白?不要绝望,这很正常。 许多东西的名称在概念上与您在其他语言中所熟悉的名称相似,但在微妙的方式上有所不同,这往往使它们真正不同。 最后,很多东西比你想象的要简单,但更奇怪(有更深的含义)

tl;博士

  • 将您的视图代码与其余部分分开,以便更容易调试
  • 使用printprobedump-face调试

这并没有直接回答您的问题,但似乎解决了您面临的问题。它使用 face/extra 字段将字段关联到您的值列表:

namelist: [var1 var2]
var1: 5
var2: 10

process: function [][
    foreach face lay/pane [
        if find namelist face/extra [
            face/text: form get to word! face/extra
        ]
    ]
]

lay: layout [ 
    text "Values to appear here: "
    field "a" extra 'var1
    field "b" extra 'var2

    button "Click" [process]
]

view lay

唯一的问题是:它应用 get 视图规范中设置的单词——它们需要与你正在处理的值处于相同的上下文中,并且——你可以得到一个 lit-word! 所以必须在得到之前改变它 to word!

另一种方法,如果你想在地图中包含你的值:

values: #(foo: 5 bar: 10)

process: function [container [object!]][
    foreach face container/pane [
        if find values face/extra [
            face/text: form select values face/extra
        ]
    ]
]

view [ 
    text "Values to appear here: "
    field "a" extra 'foo
    field "b" extra 'bar

    button "Click" [process face/parent]
]