Pull to refresh

Comments 40

Есть такой грешок :)

Но на самом деле я честно вижу мало аналогии с Brainfuck. Разве что одна — это тоже эзотерический язык.
Надо будет написать интерпретатор Quipu на Brainfuck-е.
Не думаю, что любому адекватному человеку это под силу :)
Не совсем понял, как вы переходите от узелков на нитях к символам в матрице, но сочту это недостатком моего нынешнего состояния, уверен, завтра текст будет выглядеть для меня иным. Но очень здорово видеть такие примеры пытливости человеческой мысли, с удовольствием читал рассуждения до и после.

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

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

Узел кажется совсем древним, и такую конструкцию, как на рисунке, очень легко развязывать, и менять, мне вот так кажется, что они на самом деле могли быстро вязать узлы, манипулировать ими и использовать как счеты.
>… моего нынешнего состояния, уверен, завтра текст будет выглядеть для меня иным…
Все еще под впечатлениями Алтая :-D
Сначала подумал, что $a вызывает повторное рекурсивное вычисление нити — получилась ерунда. Потом, кажется, разобрался. Интересная идея, поиграем.
"$a" — это вроде переменной с ленивой инициализацией. О рекурсии я думал. Это и был один из первых трех черновиков. Идея с ленивой инициализацией меня спасла :)
Вот это да. Знать эзотерический язык программирования — это не круто. Круто — это придумать свой интересный, нетривиальный, и, естественно, полный по Тьюрингу язык! Браво!
эзотерический_яп !=! полный_по_Т_яп
Язык не выглядит Тьюринг-полным. Программа имеет конечное число ячеек памяти и конечное число значений «счетчика команд» — вряд ли это позволит реализовать любой алгоритм. Единственная лазейка это целые числа неограниченной разрядности, на которых, теоретически, можно реализовать сколь угодно сложные структуры, но без рекурсии это тоже выглядит проблематичным. Вот если наряду с прямым чтением значения нити $x добавить её перевычисление (с возвратом по прекращению вычисления какой-нибудь нити), дела будут интереснее.
Честно говоря, не могу себе представить такую логику — возврат по вычислению какой-нибудь нити. Тут очень много неявных моментов появляется и подводных камней. Например — что делать с начальным значением — когда мы значение нити используем впервые и в самой нити. Или как понять когда надо остановиться когда очередной раз мы пытаемся рекурсивно вычислить значении нити (просто принять что call на нить вычисляет только ее одну не делая стандартного перехода на следующую нить — тоже думаю не выход).

Идея очень хорошая я тоже о ней сначала думал, но отверг из за подобных неясностей.
Сначала надо посмотреть, что получится, если логика не меняется — то есть, значение нити переприсваивается в момент выхода или перехода из неё (но не в случае вызова), и если после этого значение используется в предыдущем исполнении нити — оно уже будет новым.
Про выход — именно так: call работает, аккуратно отслеживая все переходы, но как только дойдёт до конца какой-то нити, в которой не стоит явного ?next_thread, возвращается. То есть, конец нити работает как переход в случае главной программы и как return в случае подпрограммы. Или можно сделать более логично и ввести команду return (а конец нити оставить, как переход) — это будет даже лучше. Совпадает ли return с :: — в эзотерическом языке должен совпадать, все вызванные функции обязаны завершаться нормально.
Рекурсивные числа Фибоначчи попробую написать.
Я правильно понимаю, что если интерпретировать следующий код на основе ваших рассуждений, то он будет выполнятся бесконечно?
a.

$a
&1
++


Или я что-то упустил? Можно ли вообще делать call из нити на саму себя? Будет ли он хоть когда нибудь работать и не зависать?
Я предлагаю оставить оба механизма — $a чтобы взять значение последнего вычисления нити, и ^a — чтобы вызвать перевычисление и взять получившееся значение.

Рекурсивное вычисление n-го числа Фибоначчи у меня получилось так:
a. b. c. q. e. f. r. d. n. i. j.

>> 1& ^i 1& ^f ^j :: 2& 1& ^n ^d
   --    ?b 2& ^j    $d ++ 0& ^n
   $a       ++ $n    -- ^d :: ^d
   --       << <r    :: --    0&
   $q       :: ^f       ::    :: 
   **          ^i
   $a          ++
   ++          ^f
   =e          ++
               ^i
               ++
               2&
               ++
               ::

Здесь предполагается, что ^f возвращает последнее значение, вычисленное именно нитью f (тонкий вопрос: что произойдет, если f перескочит на g, а та снова вызовет f? По логике, должно вернуться значение последнего вызванного инстанса f, чтобы ^f и последующий за ним $f давали одинаковый результат).
Небольшая тонкость — значение предыдущего вычисленного узла при вызове функции должно сохраняться (в моём примере считается, что рекурсивный вызов ^f не портит предыдущего ++).
Видно, что очень не хватает условного перехода, который бы использовал знак не последнего, а предпоследнего узла. Чтобы можно было сначала вычислить условие, потом взять результат и по условию выйти из нити (а результат сохранить в качестве результата нити).
Я к тому, что если вам удастся показать мне пример (да тот же факториал) — написанный по вашей идеи с объяснениями как такую программу интерпретировать — я буду только рад. Получится — что я придумал начало, а вы поставили отличную жирную точку :)

И тогда уже можно буде смело писать статью на esolangs.org/wiki/Main_Page.
Программа исполняется по нитям (сначала первая нить, потом вторая и т.д) сверху в низ.

А вот тут идею можно развить. Программу надо выполнять сразу параллельно по всем нитям, предусмотрев синхронизирующие узелки типа семафоров, очередей, рандеву. И можно попытаться придумать графическое представление вместо текстового — будет гораздо нагляднее.
Отличная идея для Quipu++ или Parallel Quipu :)
UFO just landed and posted this here
Такое ощущение, что однократная инициализация нитей не вписывается в общую схему: она имеет смысл только для участка кода, который исполняется ровно один раз. Стоит попытаться использовать этот участок, как функцию, или поместить его внутрь цикла — окажется, что инициализация уже не помогает, надо заменять её на более сложные механизмы, допускающие повторное использование. И зачем же она тогда вообще?
Пример: написать вычисление суммы 1^1+2^2+...+n^n :)
Без инициализации никак. Попробуйте написать вычисление факториала без инициализирующих нитей на Quipu и поймете о чем я говорю. Вам как минимум нужно присвоить начальную единицу в переменную в которой будете накапливать результат.

А ваш пример я обязательно попробую написать.
Как-то так:
n. a. b. c. q. f.

>> $b 1& $q 1& $a
   ** -- =q $q <<
   1& $n $b --
   -- -- >a >a
   $q $q
   ** **
   1& $n
   ++ ++

Здесь, конечно, используется, что на входе в блок a..q значение q равно 0, но при выходе оно тоже остается нулевым, так что код можно использовать повторно.
Кстати, вопрос. «Значение нити равно значению последнего узла» — имеется в виду значение последнего вычисления, которое до этого узла дошло? Или значение последнего вычисленного узла последнего вычисления нити? Речь о случае, когда в нити есть условные переходы, а после них какие-то вычисления.
Это прекрасно. Еще вчера у Quipu был только один пользователь — я. Уже сегодня есть программист, который пишет на нем лучше автора :)

Вы написали факториал на третьей версии языка (сейчас четвертая). Вам удалось в той версии разглядеть рабочий язык, мне (видимо из за моей узколобости) — нет. Поэтому, сейчас я всерьез задумался — не вернуть ли версию 3.

BTW, вы забыли обработку входного нуля. Т.е. 0! = 1

n. a. b. c. q. f.  o.

>> $b 1& $q 1& $a  &1
=o ** -- =q $q <<  <<
   1& $n $b --
   -- -- >a >a
   $q $q
   ** **
   1& $n
   ++ ++


На самом деле, код без инициализирующих нитей сейчас прекрасно обрабатывается текущим интерпретатором.

У меня к вам еще просьба. Не могли бы вы подобным способом написать еще пару примеров «сумма чисел от 1 до 100» и «генерация последовательности Фибоначчи.»? Буду очень признателен за помощь. Эти примеры помогут мне утвердиться во мнении, что ":" нити не нужны.

Отвечаю на ваш вопрос: значение нити равно значению последнего вычисленного на ней узелка. Т.е. даже если был переход, то нити присваивается значение узелка перед переходом.
Фраза «последнего вычисленного на ней узелка» остаётся двусмысленной: последнего по времени или по положению на нити? Если нить
a.

$q
=b
1&

вызывается сначала для q=1, а потом для q=0, то каким будет её значение при втором вызове?
Данная нить при первом вызове (q=1) будет равна «1». Перехода же не будет, поэтому вычистился последний узелок — а он — единица. При втором — будет переход и значение нити поменяется на 0 (так как последний вычисленный узелок в нити перед переходом — $q, а он равен 0).
А в какой момент фиксируется значение нити? При завершении её вычисления/переходе из неё?
Фиксируется при переходе. Есть два вида переходов — естественный — вызванный тем, что узелки на нити кончились, тогда переход осуществляется на следующую нить. Есть принудительный переход — вызываемый узелками переходов.
Вычисление последовательности Фибоначчи (с печатью) без инициализации (опять не проверялось):
a. b. z. x. y. w. v. q. e.

>> 1& $x $y $x $q $x 1& $q
   --    1& $z =v << $q >q
   $a    -- ++ ', $q -- '.
   --    $q    << >b >b <<
   $q    **    ' 
   **    1&    <<
   $a    ++
   ++
   =e
Спасибо! Это просто прекрасно.

Вот вывод программы для 10.
1, 1, 2, 3, 5, 8, 13, 21, 34, 55.


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

А вообще вы мне очень помогли. Язык без инициализирующих нитей выглядит куда более чище. Сейчас он мне очень нравится. Если бы не претензии на полноту, я бы его таковым и оставил.
Кстати почитал тут elosangs понял что у многих языков в описании стоит: " It is unknown if it is Turing-complete or not.". Думаю, что про Quipu можно что-то вроде этого и написать в его текущей реализации (без call'ов).
Еще мне кажется что проблемы полноты можно решить добавив возможность адресовывать бесконечное число нитей. Например разрешив задавать символьную метку произвольной длины. Или нумеровать нити числами 1...N, и оператор получения значения нити использовать так:

&1
$$


Получение значения нити номер 1. Или:

&4
$$ - получение значения нити номер 4
$$ - получение значения нити номер которой был записан в нити 4


Даже какая-то мнимая поддержка массивов получается.
Чтобы исходная программа была конечной длины, придётся добавить механизм создания нитей (с их кодами). Будет жутко забавно :)
Для начала можно взять пример с Excel с его абсолютной и относительной адресациями, и использовать их при клонировании…
Самое интересное, что на Scala можно написать DSL этого языка. :)
А можете кинуть ссылку о том как это сделать?
www.scala-lang.org/node/1403 вот для примера можно глянуть, можете исходники посмотреть.

Знать то особо не надо, просто использование основных фич Scala. В Scala 2.10.0 ввели макросы, можно еще круче делать.
Sign up to leave a comment.

Articles