Нашли или выдавили из себя код, который нельзя назвать нормальным,
на который без улыбки не взглянешь?
Не торопитесь его удалять или рефакторить, — запостите его на
говнокод.ру, посмеёмся вместе!
почитай как длинная арифметика делается, и используй тот же прием для перебора строки, в n-ричной системе счисления, где n - то, сколько может быть вариантов одного символа, и так можно перебирать все кобенации
Кобенации (они же пермутации) можно определить рекурсивно:
1. Все пермутации строки из одного символа - список содержащий данную строку.
2. Все пермутации строки из N символов - пермутации строки из N-1 символов где перед каждым символом добавлен N-ный символ из исходной строки + этот же символ добавлен в конце.
динамическая питухизация
компилятор сожрёт даже сладкий хлеб, и принесёт его на тарелочке в рантайм
пирфоманс
records-хуекордс
guards -- говно
отсутствие чистоты
типичная архитектура -- спагетти из кучи процессов, ожидающих невесть что, невесть откуда
нельзя инфиксные операторы определять, а значит хуй вам, а не управление control flow
VM, которой чуть-чуть не хватает проца -- обречённая VM
строки
иерархических модулей не завезли
ебанатский синтаксис
Вообще хуита, надо конечно typedef всунуть для функции, но там жопа с рекурсивным определением функции X = (int()(char **, X **))
По сути, надо чтобы функция в качестве аргумента принимала указатель того же типа, что и та сама функция, а такое рекурсивное определение типа в Си нельзя делать
А как в функцию передать указатель на функцию, которая того же самого типа, и чтобы вызвать ее оттуда? Сигнатура функции будет рекурсивной. Каст из функции которая void()(void) в функцию с любой другой сигнатурой это хоть не UB?
Или может еще нужны какие-то извраты с memcpy или union?
Это объясняет почему нельзя кастить указатель на данные в указатель на функцию. А два указателя-то на функции между собою можно кастить, если они указывают на фукнции с одинаковыми сигнатурами
Ну если подумать... В том же досе в риалмоде например была говносегментная адресация памяти, там вообще адресное пространство процессора могло быть меньше, чем фактический объем ОЗУ (640 килобайт хватит всем, ага), и там была эта питушня с near far huge указателями, там даже huge указатель в near не запихнуть (даже просто указатель на данные, без всяких там функций). Но я это говно не застал практически https://stackoverflow.com/questions/1749904/what-is-the-difference-between-far-pointers-and-near-pointers
Сегментная адресация есть не только в ДОСе в риалмоде, но и в Виндовсе/Линухе в протектедмоде, если используется PAE (page address extension). В этом случае на 32-битной машине можно использовать 36-битные указатели и адресовать 64 гига памяти вместо четырёх.
Так что кто знает, где ещё можно столкнуться с far-указателями.
но формат указателей на функцию то и указателей от этого того не изменится, что заюзали сегменты.
хотя теоретически на какой-то говноплатформе может быть call far запрещен
> 36-битные указатели и адресовать 64 гига памяти вместо четырёх.
а как это реализоано в тулчейне? 64битными становятся указательки и оттуда выдергивается сегмент?
Не приходилось пользоваться этой фичей. В защищённом режиме x86 есть селекторы и смещения. Смещение — это near-адрес, к которому все привыкли.
В регистрах CS, DS, ES, FS, GS хранится селектор (16-битное значение). Селектор — это номер записи в таблице селекторов. Запись содержит базовый адрес сегмента и лимит (в обычном режиме они 32-разрядные, в PAE имеют большую длину).
Чтобы получить far-указатель, нужно указать и селектор, и смещение, итого на 32-битной машине 16+32 бит = 48 бит. Т. е. far-указатель — это фактически запись с двумя полями.
near-указатель можно скастить в far, добавив к нему значение селектора текущего сегмента. В Винде обычно CS=DS, потому что используется «плоская» модель памяти, но в окружении с сегментированной моделью памяти селектор текущего сегмента кода и селектор сегмента данных могут не совпадать (более того, одна программа может использовать несколько сегментов), поэтому преобразование будет зависеть от контекста.
Наоборот же far в near можно преобразовать только в том случае, если селектор, записанный в far-указатель, совпал селектором «текущего» контекста. В общем случае преобразование far в near невозможно. Это как у плавающего питуха порядок отрезать.
Зачем ты базовые вещи рассказываешь? Я сам это юзал. Только вот на асме. Я спросил как обычно юзают в сишке сегменты смещение? Указатель всегда фар, либо чтоб юзать указатель надо юзать пару селектор:смещение? Я склоняюсь к атрибутам указателя void __FAR *, void __NEAR *, void __TINY * и тд. А вот это реально бажная нестандартная хрень, за которую хотелось бы долбить во все щели.
Чтобы набить ГК ключевыми словами и сюда пришли 15 троллей-ассемблерщиков.
> Указатель всегда фар, либо чтоб юзать указатель надо юзать пару селектор:смещение?
В известных реализациях были атрибуты. В Watcom C было void __far *, void __near * и void __huge *. Слово __huge использовалось только в 16-битном компиляторе и только для массивов, использующих более одного сегмента. Было ещё слово __far16, позволяющее из 32-битного кода поставить указатель на 16-битный сегмент.
Наконец, были ещё «based pointers» — когда указатель хранился как near, но в декларации был указан сегмент, так что компилятор при использовании подставлял нужный сегмент. Пример:
int __based(__segname("_CODE")) * iptr;
В самом iptr хранится только смещение, как в near-указателе, но при использовании в сегментный регистр подставится адрес секции _CODE экзешника.
Был ещё атрибут типа __seg для недоуказателя, в котором можно было хранить только селектор.
В Борманд Си и в MSVC были аналогичные атрибуты (только у MS знаков подчёркивания было поменьше).
А ещё был оператор :> для склеивания сегмента со смещением в один указатель. Обычно использовался не напрямую, а посредством макроса:
Но самое страшное, что __FAR и __NEAR — это макросы, которые в бессегментной модели памяти flat определены как пустое значение, чтобы код, написанный под сегментированную модель, компилировался без доработки.
В общем, если архитектура не гарвардская, то могут быть несовместимые указатели near/far, но внутри класса near или внутри класса far все указатели совместимы. Ах, да, near несовместим с near (по использованию, а не по формату/размеру), если сегмент не совпал.
Думается мне, как бы да - насчет гарвардской архитектуры, это конечно значит, что void* может быть не совместим с void(*)(void), но это никак не говорит нам о том, что void(*)(void) не совместим с int(_cdecl*)(int), так что вместо func* можно и нужно юзать void(*)(void). Само собой там должна быть верная сигнатура с которой ты потом будешь вызывать, но это уж из другой оперы. Временно хранить int(_cdecl*)(int) в void(*)(void) вполне себе можно
Но в данном случе проблема как раз в том что верной сигнатуры быть не может, т.к. она рекурсивная.
---
а хотя да, и проблемы нет. Просто надо вместо void** указатель на функцию сделать.
> Но в данном случе проблема как раз в том что верной сигнатуры быть не может, т.к. она рекурсивная.
с точки зрения гарвардской рахитектуры и сишки может.
Раз мы void(*)(void) юзаем вместо func*, то
например int (*)(int, func* recArg) записываем как int (*)(int, void(*recArg)(void)). Размеры там одинаковые, а перед вызовом arg мы все равно скастим к верной сигнатуре int (*)(int, func*), а именно к int (*)(int, void(*)(void)).
С точки зрения стандарта это может и лажа, но уб не бомбанет.
Нет, с точки зрения стандарта мы вызываем по неправильной, ибо int (*)(int, void(*)(void)) неправильная, в отличии от int (*)(int, SomeAccuracyFunctionType*)
Если она объявлена как int (*)(int, void(*)(void)) то это и есть правильная сигнатура. А внутри она конвертит void(*)(void) в (*)(int, void(*)(void)) и тоже вызывает по правильной.
Стандартный Паскаль совместим с гарвардской моделью, а Object Pascal (Turbo Pascal, Delphi, FPC etc) нет.
Почему? В стандартном Паскале тип указатель (включая pointer, который является аналогом сишного void *) мог указывать только на данные. Функцию можно было передавать в функцию, но с этим аргументом нельзя было ничего делать, кроме вызова. Пример:
function Integral(a, b: Real; function F(x: Real): Real): Real;
var x, result: Real;
begin
result := 0;
x := a;
while x <= b - step do
begin
result := result + F(x + step/2) * step;
x := x + step
end;
Integral := result
end;
begin
Writeln(Integral(5, 10, sin))
end.
То есть вроде как и можно было сослаться на функцию (мы сослались на sin), но поместить её адрес в переменную типа pointer нельзя. И нельзя объявить переменную, указывающую на функцию, хотя параметр функции, указывающий на функцию, объявить можно (в данном случае мы объявили F). Данный и код надёжно разделены, враг не пройдёт.
А как сделано в Object Pascal? Ключевое слово function/procedure у параметра функции теперь использовать нельзя, но его можно использовать в декларации типа.
type
myfunc = function (x: Real): Real;
function Integral(a, b: Real; F: myfunc): Real;
var x, result: Real;
begin
result := 0;
x := a;
while x <= b - step do
begin
result := result + F(x + step/2) * step;
x := x + step
end;
Integral := result
end;
begin
Writeln(Integral(5, 10, sin))
end.
Мы сделали тайпдеф и у нас F можно использовать как любую переменную. Мы можем значение параметра F скопировать в «несемантический» указатель типа pointer и наоборот:
var
p: ponter;
i: integer;
f: myfunc;
begin
f := sin;
p := @i;
p := f;
f := p;
end.
Pointer может хранить и указатели на данные, и указатели на код. Совместимость с гарвардской архитектурой в Object Pascal утеряна.
The behavior is undefined in the following circumstances:
A pointer is used to call a function whose type is not
compatible with the pointed-to type (6.3.2.3).
Так это если "is not compatible with the pointed-to type ".
Довольно логично иметь UB в случае каста указателя на функцию void foo(int) к указателю на функцию int bar(const char*) с последующим вызовом последней.
For two function types to be compatible, both shall specify compatible return types.
Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types. If one type has a parameter type list and the other type is specified by a function declarator that is not part of a function definition and that contains an empty identifier list, the parameter list shall not have an ellipsis terminator and the type of each parameter shall be compatible with the type that results from the application of the default argument promotions. If one type has a parameter type list and the other type is specified by a function definition that contains a (possibly empty) identifier list, both shall agree in the number of parameters, and the type of each prototype parameter shall be compatible with the type that results from the application of the default argument promotions to the type of the corresponding identifier. (In the determination of type compatibility and of a composite type, each parameter declared with function or array type is taken as having the adjusted type and each parameter declared with qualified type is taken as having the unqualified version of its declared type.)
В коде по ссылке - там вообще даже не указатель на функцию, а void**. Т.е. компилятор вправе форматнуть Ц, если захочет.
Кастуется в указатель на функцию, принимающую char **, void ** и возвращающую int. Ну и потом я это дело вызываю с аргументами arr_ptr-1,func_ptr-1 Так что в функцию я кастую не void** а void*
К слову, какие вообще бывают способы реализации циклов второго порядка в императивных ЯП? Ну как тут, когда цикл вложен в цикл N раз?
Сходу такие варианты:
1. Рекурсивный вызов функции.
2. Редукция до одного цикла. Общий счётчик, из которого частичные счётчики получаются делением с остатком (если диапазоны фиксированы и известны).
А можно в «Nim» наметушить цикл, который раскрывается во вложенные циклы, если есть доступ к «AST» и можно использовать шаблоны, которые типа как дефайны в сишке.
Сделать какую-нибудь компайл-тайм питушню, которая принимает массив интервалов для каждого счётчика и массив блоков кода / функций, которые будут выполняться на каждой итерации... и потом всё это будет раскрываться хитрым способом в цикл высшего порядка.
> А можно в «Nim» наметушить цикл, который раскрывается во вложенные циклы, если есть доступ к «AST»
Хуйня этот ваш "доступ к \"AST\"", надо чтоб гомоиконность. Вот в D честно признались https://govnokod.ru/27615#comment664409 что им нехватает гомоиконности как в LISP. Признались ли в этом разрабы "Nim"?
Потому что если просто доступ к AST то тогда хуй поймешь, как синтаксис отображается в AST. Т.е. нельзя удобно работать с этим вот AST потому что неочевидно, что вот этот синтаксис соответствует такому-то AST
1. Все пермутации строки из одного символа - список содержащий данную строку.
2. Все пермутации строки из N символов - пермутации строки из N-1 символов где перед каждым символом добавлен N-ный символ из исходной строки + этот же символ добавлен в конце.
Можно, только это не пермутации, а декартово возведение в степень.
Всё равно геммор, нужно же n раз заджоинить, врядли в одно строчку получится. Я даже в хаски в одну не могу... Две надо.
Вот что лямбда-зонд животворящий делает
> интуитивный и выразительный язык
Только для осиляторов
я просто дальше туториала и пошел потому что фокус языка/этц мне не подходил. erlang мне намного привлекательней.
> кококо
Скажи что он не нужен.
въебал минус
Erlang is shit, SHIT!
компилятор сожрёт даже сладкий хлеб, и принесёт его на тарелочке в рантайм
пирфоманс
records-хуекордс
guards -- говно
отсутствие чистоты
типичная архитектура -- спагетти из кучи процессов, ожидающих невесть что, невесть откуда
нельзя инфиксные операторы определять, а значит хуй вам, а не управление control flow
VM, которой чуть-чуть не хватает проца -- обречённая VM
строки
иерархических модулей не завезли
ебанатский синтаксис
sequence $ replicate n alphabet
Спасибо.
монады в моем сишарпике
http://melpon.org/wandbox/permlink/VXUNyy9R4lBf5sek
По сути, надо чтобы функция в качестве аргумента принимала указатель того же типа, что и та сама функция, а такое рекурсивное определение типа в Си нельзя делать
Или может еще нужны какие-то извраты с memcpy или union?
мне кажется memcpy + сырая память должно быть легальным, хотя не уверен
правда даже если так можно, выглядит как то малоюзабельно
http://stackoverflow.com/a/12358902 на стековерфлоу именно так и объясняют
Так что кто знает, где ещё можно столкнуться с far-указателями.
хотя теоретически на какой-то говноплатформе может быть call far запрещен
> 36-битные указатели и адресовать 64 гига памяти вместо четырёх.
а как это реализоано в тулчейне? 64битными становятся указательки и оттуда выдергивается сегмент?
В регистрах CS, DS, ES, FS, GS хранится селектор (16-битное значение). Селектор — это номер записи в таблице селекторов. Запись содержит базовый адрес сегмента и лимит (в обычном режиме они 32-разрядные, в PAE имеют большую длину).
Чтобы получить far-указатель, нужно указать и селектор, и смещение, итого на 32-битной машине 16+32 бит = 48 бит. Т. е. far-указатель — это фактически запись с двумя полями.
near-указатель можно скастить в far, добавив к нему значение селектора текущего сегмента. В Винде обычно CS=DS, потому что используется «плоская» модель памяти, но в окружении с сегментированной моделью памяти селектор текущего сегмента кода и селектор сегмента данных могут не совпадать (более того, одна программа может использовать несколько сегментов), поэтому преобразование будет зависеть от контекста.
Наоборот же far в near можно преобразовать только в том случае, если селектор, записанный в far-указатель, совпал селектором «текущего» контекста. В общем случае преобразование far в near невозможно. Это как у плавающего питуха порядок отрезать.
https://en.wikipedia.org/wiki/Physical_Address_Extension
https://ru.wikipedia.org/wiki/Дескрипторные_таблицы
Чтобы набить ГК ключевыми словами и сюда пришли 15 троллей-ассемблерщиков.
> Указатель всегда фар, либо чтоб юзать указатель надо юзать пару селектор:смещение?
В известных реализациях были атрибуты. В Watcom C было void __far *, void __near * и void __huge *. Слово __huge использовалось только в 16-битном компиляторе и только для массивов, использующих более одного сегмента. Было ещё слово __far16, позволяющее из 32-битного кода поставить указатель на 16-битный сегмент.
Наконец, были ещё «based pointers» — когда указатель хранился как near, но в декларации был указан сегмент, так что компилятор при использовании подставлял нужный сегмент. Пример:
В самом iptr хранится только смещение, как в near-указателе, но при использовании в сегментный регистр подставится адрес секции _CODE экзешника.
Был ещё атрибут типа __seg для недоуказателя, в котором можно было хранить только селектор.
В Борманд Си и в MSVC были аналогичные атрибуты (только у MS знаков подчёркивания было поменьше).
А ещё был оператор :> для склеивания сегмента со смещением в один указатель. Обычно использовался не напрямую, а посредством макроса:
Но самое страшное, что __FAR и __NEAR — это макросы, которые в бессегментной модели памяти flat определены как пустое значение, чтобы код, написанный под сегментированную модель, компилировался без доработки.
А потом придёт ещё 1 и на ГК снова не будет троллей-ассемблерщиков.
---
а хотя да, и проблемы нет. Просто надо вместо void** указатель на функцию сделать.
с точки зрения гарвардской рахитектуры и сишки может.
Раз мы void(*)(void) юзаем вместо func*, то
например int (*)(int, func* recArg) записываем как int (*)(int, void(*recArg)(void)). Размеры там одинаковые, а перед вызовом arg мы все равно скастим к верной сигнатуре int (*)(int, func*), а именно к int (*)(int, void(*)(void)).
С точки зрения стандарта это может и лажа, но уб не бомбанет.
Стандартный Паскаль совместим с гарвардской моделью, а Object Pascal (Turbo Pascal, Delphi, FPC etc) нет.
Почему? В стандартном Паскале тип указатель (включая pointer, который является аналогом сишного void *) мог указывать только на данные. Функцию можно было передавать в функцию, но с этим аргументом нельзя было ничего делать, кроме вызова. Пример:
То есть вроде как и можно было сослаться на функцию (мы сослались на sin), но поместить её адрес в переменную типа pointer нельзя. И нельзя объявить переменную, указывающую на функцию, хотя параметр функции, указывающий на функцию, объявить можно (в данном случае мы объявили F). Данный и код надёжно разделены, враг не пройдёт.
А как сделано в Object Pascal? Ключевое слово function/procedure у параметра функции теперь использовать нельзя, но его можно использовать в декларации типа.
Мы сделали тайпдеф и у нас F можно использовать как любую переменную. Мы можем значение параметра F скопировать в «несемантический» указатель типа pointer и наоборот:
Pointer может хранить и указатели на данные, и указатели на код. Совместимость с гарвардской архитектурой в Object Pascal утеряна.
Довольно логично иметь UB в случае каста указателя на функцию void foo(int) к указателю на функцию int bar(const char*) с последующим вызовом последней.
Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types. If one type has a parameter type list and the other type is specified by a function declarator that is not part of a function definition and that contains an empty identifier list, the parameter list shall not have an ellipsis terminator and the type of each parameter shall be compatible with the type that results from the application of the default argument promotions. If one type has a parameter type list and the other type is specified by a function definition that contains a (possibly empty) identifier list, both shall agree in the number of parameters, and the type of each prototype parameter shall be compatible with the type that results from the application of the default argument promotions to the type of the corresponding identifier. (In the determination of type compatibility and of a composite type, each parameter declared with function or array type is taken as having the adjusted type and each parameter declared with qualified type is taken as having the unqualified version of its declared type.)
В коде по ссылке - там вообще даже не указатель на функцию, а void**. Т.е. компилятор вправе форматнуть Ц, если захочет.
http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html
Так что позиксу срать на сишные стандарты
Конечно. Си же кроссплатформенный, а позикс только на никсах.
В коде по ссылке там void** - дважды указатель на void - но вот тут
от него отнимаем 1 и он единожды разыменовывается, в итоге получается просто void*
который потом тут
Кастуется в указатель на функцию, принимающую char **, void ** и возвращающую int. Ну и потом я это дело вызываю с аргументами arr_ptr-1,func_ptr-1 Так что в функцию я кастую не void** а void*
В одну строчку не сделать.
Тоже в одну строчку.
https://ru.wikipedia.org/wiki/Гипероператор
https://ru.wikipedia.org/wiki/Быстрорастущая_иерархия
Сходу такие варианты:
1. Рекурсивный вызов функции.
2. Редукция до одного цикла. Общий счётчик, из которого частичные счётчики получаются делением с остатком (если диапазоны фиксированы и известны).
Что ещё можно придумать?
И ещё вопрос. Бывают циклы высших порядков?
Ну и перевод на конечный автомат никто не отменял. Yield это просто сахарок для этого.
O(len*len2) вместо O(len2^len).
Сделать какую-нибудь компайл-тайм питушню, которая принимает массив интервалов для каждого счётчика и массив блоков кода / функций, которые будут выполняться на каждой итерации... и потом всё это будет раскрываться хитрым способом в цикл высшего порядка.
Хуйня этот ваш "доступ к \"AST\"", надо чтоб гомоиконность. Вот в D честно признались https://govnokod.ru/27615#comment664409 что им нехватает гомоиконности как в LISP. Признались ли в этом разрабы "Nim"?
В случае с трансформацией AST тебе надо заучивать в какую AST ноду превращается каждый элемент синтаксиса.