Как мне реализовать массив строк?
Я попытался реализовать слово, которое производит строку из массива, когда ему дано число в стеке в Forth.
Моя первая наивная попытка была:
create myarray s" Alpha" , s" Beta" , s" Charlie" ,
Это было принято, но это не сработало, как ожидалось - myarray @ type
производит противоречивый вывод (вместо моего наивного ожидания, что он может напечатать "Альфа").
При поиске в Интернете я обнаружил в документации Gforth, что строка, созданная с s"
имеет ограниченный срок службы, что означает, что мой анзац обязательно потерпит неудачу с самого начала. С другой стороны, даже массивы обычных объектов, по-видимому, не стандартизированы в соответствии с разделом " Массивы в Forth " в "Lorth Forth Tutorial".
Итак, как я могу реализовать слово, которое возвращает строку из данного массива?
2 ответа
Для решения части вашего кода, s"
листья addr u
в стеке адрес и длина строки. ,
хранит только одно значение, поэтому вы не получите желаемых результатов таким образом. 2,
может сделать это так, как бы хранить оба элемента стека, которые представляют строку. После того, как вы это сделали, вам нужно вернуть оба значения, так что 2@
это то, что вы хотите.
Мой переписать будет выглядеть так:
create myarray s" Alpha" 2, s" Beta" 2, s" Charlie" 2,
\ Test
myarray 2@ type Alpha **ok**
Получить доступ к другим элементам вашего массива немного сложнее. Когда вы печатаете myarray
вы получаете адрес начала данных в этой записи словаря, и затем вы можете использовать 2@, чтобы получить то, на что указывают первые два адреса (которые являются адресом и длиной слова "альфа"). Если вы хотите "бета", вам нужна следующая пара адресов. Так что вы можете использовать
myarray 2 cells + \ increment the address by two cells
Чтобы получить адреса, которые указывают на "Бета" и так далее. Таким образом, чтобы получить доступ к "Бета", вы должны ввести
myarray 2 cells + 2@ type Beta **ok**
Я проверил это с помощью gforth, и, похоже, все работает, хотя я не уверен, как тщательно проверять постоянство.
Ваше слово должно было бы иметь возможность увеличивать адрес в зависимости от того, что находится в стеке для начала. Вы могли бы хотеть войти в еще немного create does>
вещи. Я могу дать некоторые указания, но я не хочу портить удовольствие от открытия.
Если я пропущу слишком много деталей о том, что это на самом деле означает, просто скажи, и я попробую еще раз.
Может быть, это слишком грубо, но я попытался сделать что-то вроде "типа строки" некоторое время назад.
: string ( addr u "name" -- )
create 2, \ add address and length to dict entry "name"
does> dup cell+ @ swap @ ; \ push addr u
\ Example
s" Some Words" string words **ok**
words type Some Words **ok**
Он определяет слово с именем по вашему выбору (в данном случае "слова"), которое будет толковать длину и начальный адрес вашей строки (в данном случае "несколько слов"), когда оно будет интерпретировано. Насколько я знаю, когда строка находится в таком определении, она постоянна.
Это не полностью отвечает на ваш вопрос, но может помочь.
У меня был еще один переход на постоянную строку, этот определенно allot
s в пределах словарной записи и будет безопасным, пока это слово существует. Перед строкой "type" сохраняются только адрес и длина, которые s"
сотворенный, который хорош только тогда, когда что-то еще записывается в этой области памяти. Это теперь копирует строку откуда s"
создает его в элемент словаря с именем "имя", где он гарантированно будет длиться столько же, сколько и само "имя".
: string ( addr u "name" -- )
create \ create dict entry called "name"
dup >r here >r \ keep copies of string length and start of "name"'s memory
dup 2 cells + allot \ allot memory for the number of chars/bytes of the string plus 2
\ for the new addr u
r@ 2 cells + \ Get the address two cells from the start the space for "name"
swap cmove \ copy the string at addr u into the alloted space for "name"
\ Now "name" looks like this: "name" -blank1- -blank2- "the text of the string at addr u"
\ blank1 should be the address of the start of the the text = addr2 and blank2 should be u
r@ dup 2 cells + swap ! \ get the address of blank1, copy it, increment by 2 to get addr2
\ and then store that in blank1
r> cell+ r> swap ! \ get address of blank1, increment to get address of blank2, then get u and
\ store it in blank2
\ Now "name" looks like this: "name" addr2 u "the text of the string at addr u"
does> dup @ swap cell+ @ ; \ push addr2 u
Для развлечения я подумал, что мог бы показать, как мало это имеет смысла без полезного форматирования.
: string-no-comments ( addr u "name" -- )
create dup >r here >r dup 2 cells + allot r@
2 cells + swap cmove r@ dup 2 cells + swap !
r> cell+ r> swap ! does> dup @ swap cell+ @ ;
Во-первых. Вы должны ВЫДЕЛИТЬ постоянное хранилище для строк. В ciforth (моем Forth) есть слово $, которое делает это в словарном пространстве.
S" aap" $,
оставляет адрес с одним числом ячеек, за которым следуют символы.Стандартного слова, которое делает подобное, не существует, вы должны написать его сами.Это предполагает, что ALLOCATE недоступен. Используя это, следующий код временно сохраняет указатели на строки в стеке:
0 s" Alpha" $, s" Beta" $, s" Charlie" $,
Затем вы должны хранить там указатели в массиве, следовательно, часовой 0, за счет дополнительного вспомогательного слова:
: ttt BEGIN DUP WHILE , REPEAT DROP ;
А потом
( CREATE string-array) ttt
HERE CONSTANT ttt-end
Теперь вы можете обращаться к строкам следующим образом:
tt-end 2 CELLS - ( @+ TYPE )
Вы можете добавить вспомогательные слова. Это уродливый, громоздкий и, прежде всего, стандартный способ сделать это.