Как вводить числа в Форте

Есть ли что-то вроде input в Basic или scanf("%d") в C в Forth?

Вероятно, это будет что-то вроде этого:

200 buffer: buf
: input ( -- n ) buf 200 accept 
  some-magic-filter
  buf swap evaluate ;

Проблема в приведенном выше коде заключается в том, как определить фильтр, который будет передавать только числа, но не слова, определения и т. д.?


person ivand58    schedule 06.08.2018    source источник
comment
Как вы определяете числа в этом контексте? Целые числа (это то, что предлагает пример C - %d - это спецификатор формата для десятичного целого числа)? Вещественные числа (с плавающей запятой)?   -  person Peter Mortensen    schedule 06.08.2018


Ответы (3)


Стандарт определяет только низкоуровневое слово >NUMBER для интерпретации целых чисел. OTOH, использующий EVALUATE для преобразования строк в числа, является быстрым и грязным способом. Либо используйте его без проверок (в случае доверенного ввода), либо не используйте его вообще. Попытка отфильтровать строку до EVALUATE — плохая идея: она имеет стоимость самого слова >NUMBER и низкий коэффициент повторного использования.

Примечание: ни >NUMBER, ни EVALUATE не обнаруживают числового переполнения.

В любом случае, ваше слово для ввода целого числа из одной ячейки может быть определено примерно так:

: accept-number ( -- n )
  PAD DUP 80 ACCEPT ( addr u ) StoN ( n )
;

В случае доверенного ввода вы можете определить StoN как

: StoN ( addr u -- x )
  STATE @ ABORT" This naive StoN should not be used in compilation state"
  DEPTH 2- >R
    EVALUATE
  DEPTH 1- R> <> IF -24 THROW THEN
  \ check depth to accept the single-cell numbers only
;

В противном случае (в случае ненадежного ввода) у вас есть два варианта: полагаться на определенные слова конкретной системы Forth или использовать какую-либо (возможно, вашу собственную) библиотеку.

Я использую следующий словарь для определения StoN:

\ ---
\ The words from Substring Matching library
\ (where length is counted in address units)

: MATCH-HEAD ( a u a-key u-key -- a-right u-right true | a u false ) 
  2 PICK OVER U< IF  2DROP FALSE EXIT THEN 
  DUP >R
  3 PICK R@ COMPARE IF  RDROP FALSE EXIT THEN 
  SWAP R@ + SWAP R> - TRUE
; 

\ ---
\ The words from Literals interpreting library
\ (where prefix 'I-' is shortcut for Interpret)

: I-DLIT ( a u -- x x true | a u false ) 
  2DUP S" -"  MATCH-HEAD >R
  DUP 0= IF  NIP RDROP EXIT THEN 
  0 0 2SWAP >NUMBER NIP IF  RDROP 2DROP FALSE EXIT THEN 
  R> IF  DNEGATE THEN  2SWAP 2DROP TRUE
; 

: I-LIT ( a u -- x true | a u false ) 
  I-DLIT IF  D>S TRUE EXIT THEN  FALSE
;

После этого StoN можно определить как:

: StoN ( a u -- x ) I-LIT IF EXIT THEN -24 THROW ;

Упомянутые библиотеки можно найти на GitHub:

person ruvim    schedule 06.08.2018

Rosetta Code предлагает этот фрагмент кода, работающий с GForth 0.6.2, чтобы определить, является ли входная строка является числовым:

: is-numeric ( addr len -- )
  2dup snumber? ?dup if
   0< if
     -rot type ."  as integer = " .
   else
     2swap type ."  as double = " <# #s #> type
   then
  else 2dup >float if
    type ."  as float = " f.
  else
    type ."  isn't numeric in base " base @ dec.
  then then ;
person luigif    schedule 06.08.2018
comment
В примере C %d — это спецификатор формата для десятичного целого числа. Таким образом, усложнение с поплавками может не понадобиться. - person Peter Mortensen; 06.08.2018
comment
Кстати, snumber? не является стандартное слово и даже не задокументировано. - person ruvim; 07.08.2018
comment
На самом деле snumber? еще не задокументирован, но получен от GForth source легко понять, что это оболочка вокруг s›number?. - person luigif; 07.08.2018
comment
@luigif, да, и, поскольку это нестандартное слово, соответствующая ссылка должна быть упомянута в вашем ответе. И из-за отсутствия этой ссылки в вашем ответе я написал это в своем предыдущем комментарии :) - person ruvim; 07.08.2018

Я создал BASIC, например слово #INPUT для Camel Forth, чтобы дать пользователям BASIC что-то более знакомое. Это занимает больше, чем можно подумать. Он начинается с $ACCEPT, который можно использовать для подтверждения ввода строковой переменной или блока памяти.

ЧИСЛО? здесь только для одиночных целых чисел, но он компилируется на GForth. Выводит true, если преобразование плохое; обратная сторона SNUMBER?

    DECIMAL
    : NUMBER?  ( addr len -- n ?)      \ ?=0 is good conversion
               (          -- addr len) \ bad conversion
               OVER C@ [CHAR] - = DUP >R     \ save flag for later
               IF 1 /STRING THEN             \ remove minus sign
               0 0  2SWAP >NUMBER NIP NIP    \ convert the number
               R> IF SWAP NEGATE SWAP THEN   \ negate if needed
    ;

    : $ACCEPT ( $addr -- ) CR ." ?  "  DUP  1+ 80 ACCEPT  SWAP C!  ;
  
    : #INPUT  ( variable -- )   \ made to look/work like TI-BASIC
              BEGIN
                PAD $ACCEPT        \ $ACCEPT text into temp buffer PAD
                PAD COUNT NUMBER?  \ convert the number in PAD
              WHILE                \ while the conversion is bad do this
                 CR ." Input error "
                 CR DROP
              REPEAT
              SWAP ! ;           \ store the number in the variable

\ USAGE:  VARIABLE X  
\         X #INPUT      
 
person Brian Fox    schedule 17.06.2021