【问题标题】:Use strings to create words and paths in Red language使用字符串在 Red 语言中创建单词和路径
【发布时间】:2017-09-21 18:06:23
【问题描述】:

我在 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

【问题讨论】:

  • 您的代码结构可以更好,因此更容易理解。这将有助于分离不同的部分并将其用作您提交的所有代码中的模板
  • 您似乎正在竭尽全力去做一些不太清楚的事情。您的目标是什么?- 使用 var1var2 作为带有“单击”按钮进行重置的字段的默认值,或者您打算将字段与 var1var2 同步?从您的命名选择中并不清楚您的意图是什么。

标签: user-interface variable-assignment rebol red


【解决方案1】:

一般来说:将字符串转换为 WORD!s 很容易!(例如to-word "foo"。但是,很难神奇地制作那个单词!参考绑定到“你的意思的变量”。这样做的狡猾原因与没有范围这一事实有关。见:

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个字!元素。转换一个块!到 SET-PATH!将是这样做的一种方式。

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

现在我们看到 COMPOSE 被正确使用,它将在括号内运行评估,并单独留下 text 单词。这会生成一个包含 2 个元素的块,可以轻松地将其转换为 SET-PATH!。我使用 LOAD 而不是 TO-WORD 来处理连接到纯单词转换无法做到的实际变量的一些“魔力”。但这只是一种解决方法——不确定,也不一定能解决问题。

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

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

没有 SET-WORD!被执行,它只是生成。在这种情况下,存储在变量 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!这实际上是单独将您的整数转换为字符串,第三个是单词!将被评估为相关的整数。该块的DO会有赋值效果。

我将您的 to-word namelist/:i 更改为 load namelist/:i。同样,出于我提到的原因……TO-WORD 本身并没有“绑定”。

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

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

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

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

【讨论】:

  • 解释得很好。我们可以创建一个eval 函数,将第一部分的 to-set-path 和后续部分的 to-word 结合起来吗?
  • 如果 var1 和 var2 是“数字”5 和 10(不是字符串),我也发现很难调整您的代码。关键字to-string 放入撰写部分时会产生错误。
  • @rnso EVAL 即使在理论上也不能在 Red 的用户空间中构建,因为它将是“可变参数”,并且它不支持这一点。 eval :add 会消耗两个后续参数(意味着 EVAL 必须充当 arity 2),而 eval quote x: 只会消耗一个后续参数(EVAL 的一个矛盾定义在arity 1)。但即使你有可变参数--Ren-C does--自己编写 EVAL 仍然会很痛苦,就像在 Rebol 中重新实现 Rebol 评估器一样。
  • @rnso 我已经修改了示例,向您展示了一个可以放置 TO-STRING 的位置。现在您生成的代码块具有三个元素:一个 SET-PATH!和两个字!我将再次重申,我只是解释了这一点,试图机械地告诉你出了什么问题......也许理解会对你有益......但我会把它留给其他人和Red gitter.im或其他东西来谈论更好的方法;我不代表 Red,也不使用 GUI 方言。
  • (我会说“没有理由 Red 不能将 EVAL 添加为原生”......但是,根据经验,我会说添加到评估器中并不像似乎,特别是如果你关心效率。有很多 GC 担心为评估产品保留一个额外的单元格,支付该单元格的初始化和维护费用,这些都不是免费的......并试图采取在不破坏 eval eval eval... 之类的东西的情况下的捷径可能具有挑战性。编写一个既严谨又高效的 EVAL 比看起来更难。)
【解决方案2】:

这并不能直接回答您的问题,但似乎可以解决您面临的问题。它使用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]
]

【讨论】:

    【解决方案3】:

    第 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
    

    当我在 控制台 中运行它并按“单击”时,它会给出以下信息:

    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 红色视图 使用和您正在使用的是相同的东西:红色objects。 没有区别(都是简单人脸对象的实例)

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

    这样您就可以为您的工作创建所需的 gui,您就完成了! 对吧?

    但是你问自己,这是最好的方法吗? 如果你想add some more of thissomething else entirely怎么办?

    1. 你有read the official gui docs 尤其是关于view engine 的部分
    2. 您查看了vidadding view face objects manually 的示例
    3. 你已经在 github 上查看了 sample code and small apps 的 repo
    4. 您甚至尝试过旧的但稳定的rebol2

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

    tl;博士

    • 将您的视图代码与其他代码分开,以便于调试
    • 使用printprobedump-face进行调试

    【讨论】:

      猜你喜欢
      • 2018-04-09
      • 1970-01-01
      • 2017-07-14
      • 1970-01-01
      • 1970-01-01
      • 2018-03-08
      • 1970-01-01
      • 2018-11-16
      • 1970-01-01
      相关资源
      最近更新 更多