Используйте строки для создания слов и путей на красном языке

У меня есть строки в namelist, которые соответствуют переменным, а также именам полей в приложении.

Функция должна читать строки из списка имен, добавлять «f», чтобы получить имена полей, а затем помещать значения переменных в соответствующие поля.

Я попробовал следующий код, который не дает никаких ошибок, но также не работает:

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

person rnso    schedule 21.09.2017    source источник
comment
Ваш код можно было бы лучше структурировать, чтобы его было легче понять. Это помогло бы разделить разные части и использовать их в качестве шаблона во всем коде, который вы отправляете.   -  person Geeky I    schedule 21.09.2017
comment
Похоже, вы изо всех сил пытаетесь сделать что-то не совсем понятное. Какова ваша цель? — использовать var1 и var2 по умолчанию для полей с кнопкой «Нажмите» для сброса, или вы намерены синхронизировать поля с var1 и var2? Из вашего выбора имен не совсем ясно, каковы ваши намерения.   -  person rgchris    schedule 21.09.2017


Ответы (3)


В качестве общего замечания: строки легко превратить в WORD!s (например, to-word "foo"). Однако магическим образом составить это СЛОВО может быть сложно! ссылка будет привязана к «переменной, которую вы имели в виду». Коварные причины этого связаны с тем фактом, что отсутствует область действия. Видеть:

Есть ли общее объяснение области определения в Rebol и Red

Так что то, что вы пытаетесь сделать, в любом случае будет немного хитрым. Есть лучшие способы. Но чтобы попытаться не задавать вопрос, я объясню, что здесь происходит и как это исправить в том стиле, в котором вы пытались это сделать.

исправленная версия предназначена только для учебных целей. пожалуйста, сделайте это другим способом.

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

REJOIN применяется к блокам и объединяет содержимое с типом результата свободно на основе первого элемента. (Это сомнительная операция, но исторически популярный в коде Rebol.)

Поскольку namelist/:i является строкой, ваш REJOIN создаст строку... и эта строка будет передана в COMPOSE. Но COMPOSE предназначен для применения к BLOCK!s... и ищет группы в скобках внутри его, оценивая их, оставляя остальную часть кода в покое. Это своего рода система шаблонов для блоков, не влияющая на другие виды ввода... так что вы получите ту же самую строку.

Таким образом, TO-SET-PATH получает STRING! (например, "var1f/текст"). Я даже не знал, что преобразование пути принимает строки. Я нахожу поведение этой операции озадачивающим, потому что она, по-видимому, ЗАГРУЖАЕТ строку, а затем делает ее единственным элементом длины 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-ПУТИ! был бы способ сделать это.

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

Теперь мы видим, что COMPOSE используется правильно, где он запустит оценку внутри круглых скобок и оставит слово text в покое. Это создает блок с двумя элементами, который легко преобразуется в SET-PATH!. Я использую LOAD вместо TO-WORD, чтобы позаботиться о некоторой «магии» подключения к реальной переменной, чего не может сделать преобразование простого слова. Но это всего лишь обходной путь — ненадежный способ, и он не всегда будет решением проблемы.

Но создание SET-PATH! не значит, что он работает. Если я скажу:

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

НЕ SET-WORD! выполняется, он просто генерируется. И в этом случае хранится в переменной s. Но если бы я не сохранил его в переменной, оценочный продукт был бы просто выброшен... так же, как 2 просто выбрасывается, если я пишу 1 + 1 print "hi". Чтобы выполнить SET-PATH!, вам нужно поместить его в контекст, в котором он будет скомпонован в исходный код и оценен.

(Примечание: у Ren-C есть примитив под названием 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 строит трехэлементный блок, где первый элемент будет 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 — очень полезный инструмент, который выводит свои аргументы, а также передает их. Вы можете вставить его в различные места вашего кода, чтобы лучше понять, что происходит.

(Примечание: если вам интересно, почему я не предлагаю написать узкую вспомогательную операцию EVAL-2, которая работает только для SET-PATH!, это потому, что существует такая штука с более подходящим названием. Она называется SET. Попробуйте. set (quote x:) 10, затем print x. На самом деле варианты этого - это то, как вы на самом деле хотите что-то делать... obj: make object! [a: 10], затем set (in obj 'a) 20, затем print obj/a. Как я уже сказал, есть намного лучшие способы делать то, что вы делаете, но я пытался сосредоточиться на том, чтобы делать это так, как вы пытались.)

person HostileFork says dont trust SE    schedule 21.09.2017
comment
Очень хорошо объяснил. Можем ли мы создать функцию eval, которая сочетает путь к заданию для первой части и слово к для последующих частей? - person rnso; 22.09.2017
comment
Мне также трудно настроить ваш код, если var1 и var2 - это числа 5 и 10 (не строки). Ключевое слово to-string создает ошибку при вводе в составную часть. - person rnso; 22.09.2017
comment
@rnso EVAL не может быть встроен в пользовательское пространство в Red даже теоретически, потому что он будет вариативным, а он этого не поддерживает. eval :add будет потреблять два последовательных аргумента (это означает, что EVAL должен действовать как арность 2), в то время как eval quote x: будет потреблять только один последующий аргумент (противоречивое определение EVAL при арности 1). Но даже если у вас есть вариативные функции -- Ren-C -- написание EVAL самостоятельно все равно будет большой проблемой, как и повторная реализация оценщика Rebol в Rebol. - person HostileFork says dont trust SE; 22.09.2017
comment
@rnso Я изменил пример, чтобы показать вам место, куда вы можете поместить TO-STRING. Теперь ваш сгенерированный блок кода состоит из трех элементов: SET-PATH! и два СЛОВА! Я еще раз повторяю, что объяснил это только для того, чтобы попытаться механически объяснить вам, что пошло не так... и, возможно, это понимание принесет вам пользу... но я оставлю это другим и Red gitter.im или что-то, чтобы поговорить о лучших способах; Я не говорю за Red и вообще не работаю с диалектом GUI. - person HostileFork says dont trust SE; 22.09.2017
comment
(Я бы сказал, что нет никаких причин, по которым Red не мог бы добавить EVAL в качестве нативного... однако по опыту я скажу, что добавить в оценщик не так тривиально, как кажется, особенно если вас беспокоит У GC есть много опасений по поводу удержания дополнительной ячейки для оценочного продукта, оплаты инициализации и обслуживания этой ячейки, ни одна из которых не является бесплатной ... и попытка использовать ярлыки, не нарушая такие вещи, как eval eval eval..., может быть сложной задачей. Написать EVAL, который был бы строгим и эффективным, сложнее, чем кажется.) - person HostileFork says dont trust SE; 22.09.2017

Это не дает прямого ответа на ваш вопрос, хотя, похоже, решает проблему, с которой вы столкнулись. Он использует поле 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]
]
person rgchris    schedule 21.09.2017

Шаг 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

Имея это в виду, вот лучший способ сформулировать этот вопрос:

Шаг 3а: как динамически создавать objects с разными именами и устанавливать для них значения?

var1f/text: 5

(учитывая код на шаге 2)

Теперь я достиг загадки здесь. Это, вероятно, лучше всего задать как другой, более простой вопрос.

Я решил продолжить, предполагая, что вы добились этого (есть и другой ответ)

Примечание

На этом этапе важно усвоить, что datatype красное представление использует, и то, с чем вы работаете, одно и то же: красные objects. Нет никакой разницы (все экземпляры простого объекта лица)

Шаг 4: все готово! Или ты?

Итак, вы можете создать графический интерфейс, который вы хотите для своей работы, и все готово! Верно?

Но потом вы спросите себя, это лучший способ сделать это? Что, если вы хотите добавить еще этого или что-то еще полностью?

  1. Вы должны прочитать официальную документацию по графическому интерфейсу, особенно часть о система просмотра
  2. Вы рассмотрели примеры видео и < a href="https://github.com/red/red/blob/master/tests/view-test.red#L878" rel="nofollow noreferrer">добавление объектов просмотра face вручную
  3. Вы просмотрели репозиторий на github с примерами кода и небольшими приложениями.
  4. Вы даже пробовали старый, но стабильный rebol2.

Но ты все еще не понимаешь? Не отчаивайтесь, это нормально. У многих вещей есть имена, которые концептуально похожи на те, что вы знаете в других языках, но отличаются тонкими способами, которые, как правило, делают их действительно разными. В конце концов, многое проще, чем вы думаете, но странно (имея более глубокие последствия)

tl;dr

  • Отделите код представления от остального, чтобы его было легче отлаживать.
  • Используйте print, probe и dump-face для отладки
person Geeky I    schedule 21.09.2017