Сортировка массива строк в Forth

Я использовал CREATE для создания массива строк:

create mystringarray s" This" , s" is" , s" a", s" list" ,

И я хочу отсортировать это в порядке возрастания. Я нашел несколько руководств по ассемблеру в Интернете, но я хочу сделать это на Форте. Каков наилучший практический метод?


person Manuel Rodriguez    schedule 28.03.2018    source источник
comment
Забавный факт: еще один вопрос с теми же проблемами — Как реализовать массив строк?   -  person ruvim    schedule 20.04.2018
comment
У вас нет массива строк. В Forth нет такой вещи, как абстракция массива, которую можно было бы наложить поверх абстракции строки. Дайте мне код для печати вашей третьей строки, и вы поймете, что я имею в виду, Альберт.   -  person Albert van der Horst    schedule 19.09.2019


Ответы (2)


Сначала необходимо убедиться, что представление данных является точным.

Литеральная строка в Форте получается с использованием слова s", поэтому вы должны написать, например:

s" This"  ok

После ввода, если вы сделаете .s, вы увидите два значения:

.s <2> 7791776 4  ok

Это указатель на фактическую строку (массив символов) и подсчет количества символов в строке. Некоторые слова в Forth понимают это строковое представление. type является одним из них. Если вы сейчас введете type, вы получите строку, напечатанную на дисплее:

type This ok

Итак, теперь вы знаете, что вам нужны две ячейки для представления строки, полученной с помощью s". Ваш create должен принять это во внимание и использовать слово 2, для хранения 2 ячеек на запись, а не ,, которое хранит только одну ячейку:

create myStringArray
    s" This" 2,
    s" is" 2,
    s" an" 2,
    s" array" 2,
    s" of" 2,
    s" strings" 2,

Это массив пар адрес/счетчик для строк. Если вы хотите получить доступ к одному из них, вы можете сделать это следующим образом:

: myString ( u1 -- caddr u1 )  \ given the index, get the string address/count
    \ fetch 2 cells from myStringArray + (sizeof 2 cells)*index
    myStringArray swap 2 cells * + 2@ ;

Разбивая это, вам нужно взять базу вашей переменной массива myStringArray и добавить к ней правильное смещение к нужному строковому адресу/количеству. Это смещение представляет собой размер записи массива (2 ячейки), умноженный на индекс (который находится в стеке данных). Таким образом, выражение myStringArray swap 2 cells * +. Затем следует 2@, который извлекает двойное слово (адрес и количество) в этом месте.

Использовать...

3 myString type array ok
0 myString type This ok

и т.д...

Теперь, когда вы знаете основы индексации массива, «лучшая практика» сортировки будет следовать обычной передовой практике выбора алгоритма сортировки для типа массива, который вы хотите отсортировать. В этом случае пузырьковая сортировка, вероятно, подходит для очень небольшого массива строк. Вы должны использовать слово compare для сравнения двух строк. Например:

s" This" 0 myString compare .s <1> 0  ok

Результат 0 означает, что строки равны.

person lurker    schedule 02.04.2018
comment
Я думаю, что этот ответ должен включать какую-то реализацию сортировки, которая фактически сортирует массив (до того, как он будет принят), но я проголосовал за него, потому что это вклад в FORTH, и это само по себе потрясающе. И ваши ответы в целом потрясающие, и я хочу, чтобы вы остались. - person Evan Carroll; 07.04.2018
comment
@EvanCarroll Мое намерение состояло не в том, чтобы полностью выполнить всю работу для ОП, а в том, чтобы устранить проблемы, с которыми они столкнулись, которые помешали им завершить работу. Любая дальнейшая разработка будет переводом некоторого общепринятого алгоритма сортировки, который хорошо известен в Forth. Кроме того, существует множество подходов, выбор которых зависит от конкретного сценария сортировки OP, который не был описан. Это был очень общий вопрос, указывающий скорее на отсутствие необходимых предварительных условий для Форта, чем на саму сортировку. - person lurker; 07.04.2018
comment
@lurker, необходимо учитывать следующую проблему. Выдержка из стандарта: поскольку реализация может выбрать предоставление только одного буфера для интерпретируемых строк. , интерпретируемая строка может быть перезаписана следующим выполнением S в состоянии интерпретации - person ruvim; 14.04.2018
comment
@ruvim да, ты прав. Спасибо, что поймали это. Я обновлю свой ответ, когда у меня будет немного больше времени. - person lurker; 14.04.2018
comment
Простой способ решить проблему временного буфера — обернуть строки как :noname s" This" 2, s" is" 2, ... ; execute - person ruvim; 14.04.2018

Лучший метод сортировки массива — использовать какую-нибудь существующую библиотеку. Если существующие библиотеки вам не подходят или ваша основная цель — обучение — тогда имеет смысл реализовать собственную библиотеку.

Использование библиотеки

Например, модуль массива ячеек из The Forth Foundation Library (FFL) можно использовать для сортировки массива любых элементов.

Пример кода

include ffl/car.fs
include ffl/str.fs

0 car-new value arr  \ new array in the heap

\ shortcut to keep -- add string into our 'arr' array
: k ( a1 u1 -- ) str-new dup arr car-push str-set ;

\ set compare method
:noname ( a1 a2 -- n ) >r str-get r> str-get compare ; arr car-compare!

\ dump strings from the array
: dump-arr ( -- ) arr car-length@ 0 ?do i arr car-get str-get type cr loop ;

\ populate the array
s" This" k s" is" k s" a" k s" list" k

\ test sorting
dump-arr cr
arr car-sort 
dump-arr cr

Выход

This
is
a
list

This
a
is
list

Использование голого Форта

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

Массив строк должен содержать только адреса строк. Сами струны должны храниться в каком-то другом месте. В этом случае полезно использовать формат строки со счетом — поэтому мы используем слово c" для строковых литералов. Чтобы сохранить сами строки, мы помещаем код инициализации в определение (в данном случае :noname) — он сохранит строки в пространстве словаря.

Пузырьковая сортировка адаптирована из варианта для чисел в вариант для строк только с заменой слова для сравнения элементов. Обратите внимание, что слово 2@ возвращает значение наименьшего адреса сверху.

Пример кода

\ some helper words
: bounds ( addr1 u1 -- addr1 addr2 ) over + swap ;
: lt-cstring ( a1 a2 -- flag ) >r count r> count compare -1 = ;

\ create an array of counted strings
:noname ( -- addr cnt )
  here
    c" This" , c" is" , c" a" , c" list" ,
  here over - >cells
; execute constant cnt constant arr

\ dump strings from the array  
: dump-arr ( -- ) cnt 0 ?do i cells arr + @ count type cr loop ;

\ bubble sort
: sort-arr ( -- )
  cnt 2 u< if exit then
  cnt 1 do true
    arr cnt i - cells bounds do
      i 2@ ( a2 a1 ) lt-cstring if i 2@ swap i 2! false and then
    cell +loop
    if leave then
  loop
;

\ test sorting
dump-arr cr
sort-arr
dump-arr cr

\ the output is the same as before
person ruvim    schedule 14.04.2018