Юмористический ответ на вопрос, заданный в сабреддите Flutter о лучшем языке для мобильных приложений.
Лучший язык программирования — Форт по понятным причинам. Следовательно, это также лучший язык программирования для разработки мобильных приложений. Мы можем использовать Dart для его реализации.
Давайте определим
typedef Impl = void Function(Forth f);
и создайте класс с именем Forth
, который инкапсулирует stack
, для которого мы можем push()
значения и из которого мы можем pop()
значения
class Forth { final stack = <dynamic>[]; void push(dynamic value) => stack.add(value); dynamic pop() => stack.removeLast();
и это определяет так называемый словарь предопределенных слов (типа Impl
)
final Map<String, Impl> vocab = { '+': (f) => f.push(f.pop() + f.pop()), '.': (f) => print(f.pop()), };
Также определите метод run()
для выполнения слов одно за другим:
List<String> words = []; void run(String input) { words = input.split(' '); while (words.isNotEmpty) { final word = words.removeAt(0); final impl = vocab[word] ?? lit(num.parse(word)); impl(this); } }
Метод lit
автоматически преобразует слова, которые выглядят как числа, в экземпляры Impl
, которые помещают значение этого числа в стек, так что все числа неявно предопределены.
Impl lit(dynamic value) => (f) => f.push(value);
что завершает определение класса Forth
}
Теперь мы можем выполнять такие выражения, как Forth().run(‘1 2 3 + + .’)
, чтобы напечатать 6.
Новые слова
Нам нужен способ определить наши собственные слова, такие как : twice dup + ;
, для которых нам нужно расширить класс Forth
, добавив :
и ;
.
class Forth { ... List<Impl>? code; Map<String, Impl> vocab = { ... ':': (f) { final name = f.words.removeAt(0); final code = <Impl>[]; f.vocab[name] = (f) => code.forEach((impl) => impl(f)); f.code = code; }, ';': (f) => f.code = null,
а также dup
, который также использовался в моем примере выше
'dup': (f) => f.push(f.stack.last), }
и нам нужно изменить run
, чтобы не сразу вызывать каждый экземпляр Impl
при компиляции кода, а собирать эти объекты в списке, настроенном :
... void run(String input) { words = input.split(' '); while (words.isNotEmpty) { final word = words.removeAt(0); final impl = vocab[word] ?? lit(num.parse(word)); if (code != null && word != ';') { code!.add(impl); } else { impl(this); } } }
На самом деле, мы должны различать обычные Impl
и немедленные Impl
, такие как ;
, которые должны выполняться даже при компиляции, но я жестко запрограммировал их. Через минуту мне также нужно будет сделать "
, [
и ]
немедленными.
Новые приложения
Тем не менее, я хвастался использованием Forth для создания мобильного приложения. Вот код популярного приложения-счетчика
V: count 5 ! : inc dup @ 1 + ! ; [ [ text ] count builder [ count inc ] " Increment text button ] list column app run
что должно быть легко понять. Я определяю переменную с именем count
, присваивая 5. Затем я определяю слово inc
, которое можно применить к такой переменной, чтобы увеличить ее значение на 1, а затем я определяю виджет column
с виджетом text
, построенным из значения переменной, и виджетом button
который делает приращение, обернутое как приложение.
Начнем с V:
. который определяет слово, которое помещает ValueNotifier
в стек, а также помещает этот экземпляр, чтобы его было легче инициализировать
'V:': (f) { final name = f.words.removeAt(0); final value = ValueNotifier<dynamic>(null); f.vocab[name] = f.lit(value); f.push(value); },
Слово !
ожидает ValueNotifier
и значение в стеке, удаляет оба и присваивает значение уведомителю, например:
'!': (f) => ((dynamic v) => (f.pop() as ValueNotifier<dynamic>).value = v)(f.pop()),
Слово @
также ожидает ValueNotifier
в стеке, которое заменяется значением уведомителя.
'@': (f) => f.push((f.pop() as ValueNotifier<dynamic>).value),
Слова [
и ]
снова являются непосредственными словами, которые работают даже при составлении слова с :
. Однако вместо компиляции Impl
экземпляров в текущее определяющее слово создается новый список, так что run
записывается в так называемую цитацию, которая затем формирует блок кода, который помещается в стек и может быть явно выполнен позже.
'[': (f) { f.push(f.code); f.code = <Impl>[]; }, ']': (f) { final quot = f.code; if (quot == null) throw Exception('] without ['); f.code = f.pop() as List<Impl>?; f.add(quot); },
Слово "
также является немедленным словом, которое помещает следующее слово в стек, не выполняя его, то есть помещает только строку.
'"': (f) => f.add(f.words.removeAt(0)),
Вот новый метод add
:
void add(dynamic value) => code != null ? code!.add(lit(value)) : push(value);
Чтобы иметь дело с расширяемым списком непосредственных слов, давайте изменим run
... final immediate = {';', '"', '[', ']'}; void run(String input) { words = input.split(' '); while (words.isNotEmpty) { final word = words.removeAt(0); final impl = vocab[word] ?? lit(num.parse(word)); if (immediate.contains(word) || code == null) { impl(this); } else { code!.add(impl); } } }
Чтобы позже выполнить котировку, мы используем этот метод exec
:
void exec(List<Impl> quot) { for (final impl in quot) { impl(this); } }
Давайте создадим виджеты.
Слово builder
ожидает ValueNotifier
и кавычку и настраивает виджет ValueListenableBuilder
Flutter, чтобы кавычка выполнялась каждый раз, когда изменяется значение уведомителя.
'builder': (f) { final vl = f.pop() as ValueNotifier<dynamic>; final quot = f.pop() as List<Impl>; f.push(ValueListenableBuilder( valueListenable: vl, builder: (context, value, _) { f.push(value); f.exec(quot); return f.pop() as Widget; }, )); },
Затем нам нужны слова для создания виджетов Flutter Text
, Column
и MaterialApp
, которые на данный момент не должны содержать сюрпризов.
'text': (f) => f.push(Text('${f.pop()}')), 'button': (f) { final child = f.pop() as Widget; final quot = f.pop() as List<Impl>; f.push(ElevatedButton(onPressed: () => f.exec(quot), child: child)); }, 'column': (f) => f.push(Column(children: (f.pop() as List).cast<Widget>())), 'app': (f) => f.push(MaterialApp(home: Scaffold(body: f.pop() as Widget))),
И последнее, но не менее важное: мы можем запустить виджет Flutter в стеке:
'run': (f) => runApp(f.pop() as Widget),
И это все, что нам нужно для создания работающего приложения Flutter в Forth.
Если вам интересен пример с бегом, вот коврик для дротиков.