LINUX.ORG.RU

Не учите Common Lisp!

 ,


1

7

Хочу предостеречь форумчан от изучения Common Lisp. Возьмите самую лучшую имплементацию — SBCL и скомпилируйте следуюущие лямбды:

;; Взятие элемента по индексу x из массива x
(lambda (x) (aref x x))
;; Взятие элемента по индексу y из массива x и прибавление к x
(lambda (x y) (+ x (aref x y)))
;; Один из путей выполнения: прибавление X к NIL
(lambda (x y) (+ x (funcall (lambda (x) (if x 0 x)) y)))

Ни на одной из этих бессмысленных функций компилятор не ругнётся. Вы скажете: «А что такого? Это же язык с динамической типизацией!» А то, что если вы хотите хоть насколько нибудь быстрого кода, вам нужно делать какие-то утверждения о типах, производя только лишь статический анализ кода.

Минус коммон лиспа как языка — слишком разрешательский стандарт. Почти сплошь и рядом там фразы вроде «Эффект, производимый объявлением inline, определяется реализацией» или «Реализация вольна игнорировать то-то и вместо этого делать то-то». Из-за этого разработчики того же SBCL будут игнорировать твои баг репорты, где из-за неправильного поведения компилятора (с точки других опубликованных алгоритмов) твой код будет работать раза в 2 или 3 медленнее, чем должен бы.

Написали бы в стандарте «реализация ДОЛЖНА поступать так-то и так-то», и можно было бы тыкать разрабов носом в это место и требовать корректного поведения. А раз написали «реализация может делать, что ей захочется», то и прижучить их нельзя. В итоге все занимаются чем хотят, кроме починки багов.

Короче, учите языки с хорошей теоретической базой и статической типизацией. Байкам @lovesan не верьте ни одной.



Последнее исправление: dura4ok11 (всего исправлений: 1)
Ответ на: комментарий от ugoday

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

С и С++ превратились в разные языки, да.

Неразобрал эту фразу. Что вы называете «подключаться к компилятору»?

Использовать внутренние функции компилятора, нестандартизированные.

Что такое команда? Чем она должна отличаться от функции или макроса?

Опкод, чем он отличается от макроса надеюсь понятно, если реально нет, то объясняю, ты можешь сделать функцию (defun + (int int) (moya-novaya-instructia)) но как компилятор будет оптимизировать это на уровне машинного кода? Никак, потому что у него нету информации вместо чего ее надо вставить, какая у нее стоимость, когда лучше вставить другую, на что ее можно поменять.

Он может оптимизировать (defun add (int int) (+ int int)), лишь потому что такая функция преобразуется в понятные ему команды.

А почему? Чего не хватает?

Нужен как минимум задокументированный, более менее стабильный API для доступа ко внутренностям компилятора.

https://www.lispworks.com/documentation/HyperSpec/Body/m_defmac.htm

На этом нельзя добавить в компилятор новый опкод. Только аналог сишного #define some __asm__(...)

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 5)
Ответ на: комментарий от MOPKOBKA

На этом нельзя добавить в компилятор новый опкод

А на сишке, значит, можно добавить процессору новый опкод, который он раньше не поддерживал? %)

Смысл синтаксической абстракции (макросов) не в «добавлении новых опкодов».

Nervous ★★★★★
()
Последнее исправление: Nervous (всего исправлений: 1)
Ответ на: комментарий от Nervous

А на сишке, значит, можно добавить процессору новый опкод, который он раньше не поддерживал? %)

Не в процессор, а в компилятор.

Смысл синтаксической абстракции (макросов) не в «добавлении новых опкодов».

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

MOPKOBKA ★★★★★
()
Ответ на: комментарий от MOPKOBKA

С и С++ превратились в разные языки, да.

А почему? Что мешало оставить С++ в виде препроцессора для С? Может быть какая-то причина была, не от балды же это сделали.

Использовать внутренние функции компилятора, неестандартизированные.

Я только что расширил язык, алё. Там раньше не было метаданных у структур, теперь они есть у нас.

(defun + (int int) (moya-novaya-instructia)) но как компилятор будет оптимизировать это на уровне машинного кода?

Никогда этим не интересовался. Виртуальные операции не являются ассемблерными инструкциями в прямом смысле, они как-то в них переводятся и, возможно (т.е. тут я предполагаю), тут можно сделать подсказки компилятору. Ну, а если и нет — мы всё равно имеем, что в лисп можно добавить новый синтаксис, новую семантику и специфицировать в какой машинный код это всё раскроется. И нам не нужно для этого ни писать плагин к компилятору (чего бы это ни значило), ни ждать решения комитета, ни даже лисп-систему перезапускать.

Нужен как минимум задокументированный, более менее стабильный API для доступа ко внутренностям компилятора.

Зачем? Меня не оставляет ощущение, что вы пытаетесь описать лисп-систему в терминах, которые вообще для неё не подходят, получается какой-то тягостный бред.

На этом нельзя добавить в компилятор новый опкод.

Вы как-то уцепились за эту тему, но ведь расширять языки можно и без новых опкодов. Или скажете «С с классами» не является расширением С?

ugoday ★★★★★
()
Ответ на: комментарий от MOPKOBKA

ты совсем не понимаешь про что я говорю.

Я, кстати, тоже временами теряюсь.

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

А почему? Что мешало оставить С++ в виде препроцессора для С? Может быть какая-то причина была, не от балды же это сделали.

Можно начать с другой модели памяти...

Я только что расширил язык, алё.

Я с этим спорил?

Ну, а если и нет — мы всё равно имеем, что в лисп можно добавить новый синтаксис, новую семантику и специфицировать в какой машинный код это всё раскроется.

По нормальному, только если залезть в компилятор, так же как в С. А просто написать макрос не получится.

Мне неинтересно что в лиспе можно какие то макросы писать в одном файле рядом с кодом проекта, это не важно в моем обсуждении.

Зачем? Меня не оставляет ощущение, что вы пытаетесь описать лисп-систему в терминах, которые вообще для неё не подходят, получается какой-то тягостный бред.

Я не пытаюсь описать лисп систему, ты не задавал вопрос «что такое лисп система», и я нигде не говорил что текст выше это описание лисп-системы.

Вы как-то уцепились за эту тему, но ведь расширять языки можно и без новых опкодов. Или скажете «С с классами» не является расширением С?

Перечитай мой комментарий Не учите Common Lisp! (комментарий)

Там я говорю что для определенных изменений свойства лиспа никак не помогают, и после я показываю что это за изменения.

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

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 5)
Ответ на: комментарий от MOPKOBKA

Можно начать с другой модели памяти…

Простите, вы кажется говорили, что препроцессора будет достаточно. Теперь это уже не так?

Мне неинтересно что в лиспе можно какие то макросы писать в одном файле рядом с кодом проекта, это не важно в моем обсуждении.

Мы обсуждаем возможность расширить язык. По крайней мере мне так казалось. Теперь, оказывается, это уже не важно. Ну, ладно.

Перечитай мой комментарий Например добавление новых fpu операций, недостаточно просто перегрузить операторы парой строчек,

Я просто оставлю это здесь: https://github.com/angavrilov/cl-simd. Если добавление инструкций SSE вас не впечатляет, то уж не знаю чем и угодить.

такие просто решаются и #define,

#define годится только для самых-пресамых примитивных случаев.

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

Я просто оставлю это здесь: https://github.com/angavrilov/cl-simd. Если добавление инструкций SSE вас не впечатляет, то уж не знаю чем и угодить.

void func(int *a) {
    a[7] = 7;
    a[6] = 6;
    a[5] = 5;
    a[4] = 4;
    a[3] = 3;
    a[2] = 2;
    a[1] = 1;
    a[0] = 0;
}

компилируется в

func(int*):
        movdqa  xmm0, XMMWORD PTR .LC0[rip]
        movups  XMMWORD PTR [rdi], xmm0
        movdqa  xmm0, XMMWORD PTR .LC1[rip]
        movups  XMMWORD PTR [rdi+16], xmm0
        ret
(defun f (a)
           (declare (type (simple-array (unsigned-byte 32) (*)) a))
           (setf (aref a 7) 7
                 (aref a 6) 6
                 (aref a 5) 5
                 (aref a 4) 4
                 (aref a 3) 3
                 (aref a 2) 2
                 (aref a 1) 1
                 (aref a 0) 0))

компилируется в

; disassembly for F
; Size: 85 bytes. Origin: #xB800C8FD61                        ; F
; 61:       498B4D10         MOV RCX, [R13+16]                ; thread.binding-\
stack-pointer
; 65:       48894DF8         MOV [RBP-8], RCX
; 69:       488378F90E       CMP QWORD PTR [RAX-7], 14
; 6E:       763F             JBE L0
; 70:       C7401D07000000   MOV DWORD PTR [RAX+29], 7
; 77:       C7401906000000   MOV DWORD PTR [RAX+25], 6
; 7E:       C7401505000000   MOV DWORD PTR [RAX+21], 5
; 85:       C7401104000000   MOV DWORD PTR [RAX+17], 4
; 8C:       C7400D03000000   MOV DWORD PTR [RAX+13], 3
; 93:       C7400902000000   MOV DWORD PTR [RAX+9], 2
; 9A:       C7400501000000   MOV DWORD PTR [RAX+5], 1
; A1:       C7400100000000   MOV DWORD PTR [RAX+1], 0
; A8:       31D2             XOR EDX, EDX
...

Как сделать так, чтобы setf использовал cl-simd?

monk ★★★★★
()
Ответ на: комментарий от ugoday

мы всё равно имеем, что в лисп можно добавить новый синтаксис, новую семантику

Новый синтаксис — да. Новую семантику — нет. Например, call/cc в SBCL воткнуть невозможно. В виртуальной машине нет понятия «продолжения» и добавить никак.

monk ★★★★★
()
Ответ на: комментарий от ugoday

В сишке для этого пришлось писать С++

Создатели Gtk с тобой поспорят. Да и на ЛИСПе tinyclos, например, написан без всяких макросов.

no-such-file ★★★★★
()
Ответ на: комментарий от monk

Как сделать так, чтобы setf использовал cl-simd?

Очевидно нужен макрос, который будет преобразовывать setf в simd. В итоге код должен выглядеть так

(defun f (a)
(with-simd 
           (declare (type (simple-array (unsigned-byte 32) (*)) a))
           (setf (aref a 7) 7
                 (aref a 6) 6
                 (aref a 5) 5
                 (aref a 4) 4
                 (aref a 3) 3
                 (aref a 2) 2
                 (aref a 1) 1
                 (aref a 0) 0)))

Реализацию данного макроса оставим читателям в качестве домашнего задания.

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от ugoday

Можно начать с другой модели памяти…

Простите, вы кажется говорили, что препроцессора будет достаточно. Теперь это уже не так?

Я говорил прямо противоположенное.

Мы обсуждаем возможность расширить язык. По крайней мере мне так казалось. Теперь, оказывается, это уже не важно. Ну, ладно.

Да, обсуждали.

Я просто оставлю это здесь: https://github.com/angavrilov/cl-simd. Если добавление инструкций SSE вас не впечатляет, то уж не знаю чем и угодить.

Конечно #define simd __asm__ меня не впечатляет, я просил совершенно другое. Например что бы другой код без доработок начал компилироваться с автовекторизацией.

#define годится только для самых-пресамых примитивных случаев.

Ровно тоже самое я написал тебе два раза.

MOPKOBKA ★★★★★
()
Ответ на: комментарий от rtxtxtrx

а так ли важно на чем написан похапе? в итоге мы имеем машинные инструкции.

Я также считаю. Просто собеседник, похоже, придерживается другого мнения.

Obezyan
()
Ответ на: комментарий от MOPKOBKA

Конечно #define simd asm меня не впечатляет, я просил совершенно другое. Например что бы другой код без доработок начал компилироваться с автовекторизацией.

Это довольно сложная тема которую просто на словах будет тяжело обсудить и тем более понять. Предлагаю взять какую-нибудь несложную функцию (как единицу оптимизации), например - цикл с инкрементами и показать на примере о чем речь. Тогда собеседники смогут попробовать реализовать это на их языках.

Obezyan
()
Ответ на: комментарий от Obezyan

Я уверен ugoday знает что такое автовекторизация, и ему примеры не нужны. Если нет, их полно в интернете.

MOPKOBKA ★★★★★
()
Ответ на: комментарий от monk

Новую семантику — нет.

Слишком сильное утверждение. Скорее так: новую семантику не любую. Но, поскольку у остальных и того нет, то и так неплохо вышло.

ugoday ★★★★★
()
Ответ на: комментарий от no-such-file

А альпинисты поспорят, что по скалам лазать неудобно. Фигли, десять лет упорных тренировок, $50т на экипировку и Эверест твой.

Я же не вижу принципиальной разницы между «нельзя» и «можно, но неудобно».

ugoday ★★★★★
()
Ответ на: комментарий от MOPKOBKA

Я вижу, вы решили отступить от «расширение языка» в более узкую область «переопределение некоторых аспектов поведения компилятора» и крепко держать там оборону. Бог в помощь. Так вышло, что байтолюбство и хитрые способы быстро перемножать матрицы никогда не находились в фокусе моих интересов. Надеюсь, другие участники беседы смогут поддержать её на высоком профессиональном уровне. А нет — я это переживу, да и вы тоже.

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

Я вижу, вы решили отступить от «расширение языка»

Нет, просто остановился на понятном мне примере, вот monk выше привел другой пример с продолжениями.

Что препроцессор, что написание плагинов это расширения языка, разве нет?

Выше ты писал про реализацию макроса loop в С, но его сложность вовсе не в том что он макрос, а в том что его реализация занимает несколько тысяч строк, с кучей sbcl-специфичных вставок, и это все с учетом что в CL намного больше высокоуровневых структур из коробки чем в С. defmacro может сделать элегантным некоторый код, но когда этот defmacro лишь 1% от всего кода, так ли это важно?

Если в какой то кодовой базе всего один раз используется какой нибудь VLA массив, странно говорить что без VLA массива написать проект было бы невозможно, или что он значительно ускорил/улучшил процесс создания. Без VLA, впихнули бы alloca() и посчитали бы индексы, так же и в С с макросами.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 2)
Ответ на: комментарий от MOPKOBKA

Нет, просто остановился на понятном мне примере,

Боюсь тут наши области интереса не совпадают, данный разговор я могу поддержать на уровне среднего таксиста: „Так-то я пишу оптимизирующие компиляторы для верилога, а ямлы на работе правлю просто так, для души“.

другой пример с продолжениями.

Тут другая трудность. Непонятно как совмесить продолжения и unwind-protect (типа finally в яве). Т.е. можете форкнуть sbcl и прямо в коде писать что хотите хоть на CL, хоть на С, вот только непонятно чего писать-то.

его сложность вовсе не в том что он макрос, а в том что его реализация занимает несколько тысяч строк, с кучей sbcl-специфичных вставок, и это все с учетом что в CL намного больше высокоуровневых структур из коробки чем в С.

Это, пожалуй, основной пункт моего утверждения. Если что-то можно сделать легко, скорее всего это будет сделано. А если сложно — то не будет. Соответственно простота и удобство инструмента определяет ту грань, которая отделяет реализуемые расширения от нереализуемых. Метапрограммирование в стиле лиспа смещает эту грань сильно, поэтому loop на лиспе есть, а аналога на C — нету.

когда этот defmacro лишь 1% от всего кода, так ли это важно?

А это как считать. Если данные макросы используются во всём остальном коде, то тут можно к коду макросов прибавить и код всех использующих их функций. Опять же, бывает и так: STMX: high-performance implementation of composable Transactional Memory (TM) for Common Lisp. Здесь defmacro и defun представлены в примерно равной пропорции.

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

Скорее так: новую семантику не любую.

Новую семантику, не реализованную в компиляторе. Если новая семантика представима существующими структурами (как замыкание и структура могут представлять объекты ООП), то проблемы нет.

Но, поскольку у остальных и того нет, то и так неплохо вышло.

По реализации семантики возможности примерно одинаковые (GObject против CLOS). Лисп даёт легко создать новый синтаксис вплоть до DSL для одной функции. В других языках приходится использовать генераторы.

monk ★★★★★
()
Ответ на: комментарий от ugoday

Непонятно как совмесить продолжения и unwind-protect (типа finally в яве).

В Scheme есть dynamic-wind.

Т.е. можете форкнуть sbcl и прямо в коде писать что хотите хоть на CL, хоть на С, вот только непонятно чего писать-то.

Если форкнуть, то можно. Но всё равно сложно, придётся внутреннее представление замыкания менять. Проще на какой нибудь Scheme реализовать ANSI CL.

Метапрограммирование в стиле лиспа смещает эту грань сильно, поэтому loop на лиспе есть, а аналога на C — нету.

Аналог на C называется «компилятор» :-). Или транспилятор в Си.

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

monk ★★★★★
()
Ответ на: комментарий от ugoday

Это, пожалуй, основной пункт моего утверждения.
Метапрограммирование в стиле лиспа смещает эту грань сильно, поэтому loop на лиспе есть, а аналога на C — нету.

Я считаю что loop нету по другим причинам:

1) Сложность, в CL это стандартная конструкция, обязательная к реализации, есть ли альтернативные реализации помимо тех что есть в компиляторе?

2) Отсутствие понятие итерируемости у объектов

А свои конструкции циклов на С делают постоянно, пример из ядра:

list_for_each_entry_safe_reverse(pos, tmp, head, miscj)
        kobject_del(&pos->kobj);

А это как считать. Если данные макросы используются во всём остальном коде, то тут можно к коду макросов прибавить и код всех использующих их функций

Если альтернативная реализация без макроса не может обеспечить схожие возможности за примерно тоже самое количество строк, то можно считать.

Здесь defmacro и defun представлены в примерно равной пропорции.

Осталось понять их сложность, и можно ли их заменить на #define, вот например взял первый попавшийся макрос

(defmacro atomic-incf (place &optional (delta 1))
  "Atomically increment PLACE by DELTA. Return _previous_ value of PLACE."
    `(sb-ext:atomic-incf ,place ,delta))
Это ведь можно заменить на:
#define atomic_incf(place, delta) sb_ext_atomic_incf(place, delta)
???

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA
  1. Да. cl-iterate.

  2. Сила loop не в итерируемости, а в условиях и аккумуляторах.

Попробуй сам переписать

(setf list-sums
  (loop for i in list1
        for j in list2
        when (> i j)
        collect (+ i j)))

на for (i : list1) { .... }.

monk ★★★★★
()
Ответ на: комментарий от MOPKOBKA

Это ведь можно заменить на:

Это можно. Но это вырожденный случай. Как определение констант через define.

monk ★★★★★
()
Ответ на: комментарий от monk

Я не знаю как это исполняется, но если это проход одновременно по двум спискам и сумма их элементов то так

int sum = 0;
for (int i = 0, j = 0; 
     i < arr1_size && j < arr2_size 
     && arr1[i] > arr2[j]; 
     i++, j++)
  sum += arr1[i] + arr2[j]; 

Возможно when влияет только на захват элементов, тогда так

int sum = 0;
for (int i = 0, j = 0; i < arr1_size && j < arr2_size; i++, j++)
  if (arr1[i] > arr2[j]) sum += arr1[i] + arr2[j]; 

Или вот так если украсить макросами

with(i) with(j) loop
  over(i, arr1, arr1_len) // if (i >= n) break
  over(j, arr2, arr2_len)
  when(arr1[i] > arr2[j]) // if (!(cond)) continue
  when(arr1[i] < 100 && arr2[j] < 100)
    sum += arr1[i] + arr2[j];
done

Если макросами конструкция не будет покрываться, то подключаются лямбды, и __VA_ARGS__, опциональные аргументы задаются как в printf, ну это будет примерно тоже самое что и pipe на PHP выше.

Это можно. Но это вырожденный случай. Как определение констант через define.

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

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 13)
Ответ на: комментарий от MOPKOBKA

в CL это стандартная конструкция, обязательная к реализации, есть ли альтернативные реализации помимо тех что есть в компиляторе?

CL это 15-й стандарт из https://xkcd.com/927/. Вот, в других лиспах были разные реализации для циклов, а тут упорядочили.

Это ведь можно заменить на:

Я, пожалуй, выскажусь на эту тему в последний раз, а то уже на третий круг пошли. Моя позиция простая как правда: что бывает, то и может быть, а чего не может быть, того уж никак не бывает. Соответственно, мы не пытаемся гадать, опираясь на свойства исследуемых компонент, а непосредственно смотрим на реальность. В реальности развитие С идёт по принципу «комитет придумал стандарт → gcc выпустила новую версию». #define же используется для подстановки платформоспецифичных констант или подобной мелочи. С++ свои шаблоны тоже не от безделья придумал. Реальность такова, что #define не подходит для сложных штук. Спорить с реальностью можно, но это, пожалуй, без меня.

P.S. Про разные реализации циклов, посмотрите примеры https://lispcookbook.github.io/cl-cookbook/iteration.html

ugoday ★★★★★
()
Последнее исправление: ugoday (всего исправлений: 1)
Ответ на: комментарий от ugoday

С++ свои шаблоны тоже не от безделья придумал.

Видимо Lisp тоже не подходил. Буду знать что у С++ шаблоны намного лучше.

В реальности развитие С идёт по принципу ...

В реальности я скинул примеры макросов для циклов, плагины для компилятора и показал пример с enum.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

но если это проход одновременно по двум спискам и сумма их элементов то так

Ты же про итераторы говорил, а не про индексацию всех коллекций.

Хотя первый вариант подстрочно останется тем же самым.

with(i) with(j) loop

Это реальный макрос? Читается почти как cl:loop.

monk ★★★★★
()
Ответ на: комментарий от monk

В Scheme есть dynamic-wind.

А в лиспе https://cl-cont.common-lisp.dev/

Но у лисповых продолжений есть недостаток:

the following limitation: call/cc cannot appear within the body of catch, throw, progv, or unwind-protect.

А к dynamic-wind тоже имеется критика: https://news.ycombinator.com/item?id=16720729

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

ugoday ★★★★★
()
Ответ на: комментарий от monk

Ты же про итераторы говорил, а не про индексацию всех коллекций.

Не понял про что ты. Я говорил что нету итерируемости у встроенных объектов, в том смысле что нету итераторов как отдельных объектов, все что есть это смещение указателей.

Это реальный макрос? Читается почти как cl:loop.

Да.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 2)
Ответ на: комментарий от ugoday

А в лиспе https://cl-cont.common-lisp.dev/

Ты его видел? Я пытался использовать: он как интерпретатор работает.

А к dynamic-wind тоже имеется критика: https://news.ycombinator.com/item?id=16720729

То ли ссылка не та… dynamic-wind на той странице нет.

monk ★★★★★
()
Ответ на: комментарий от MOPKOBKA

все что есть это смещение указателей

Так это и есть итераторы. По крайней мере в Си++ foreach на обобщённых указателях и сделан.

Или я вообще понял не так. Я понял, что в CL нет итераторов, поэтому сделать loop (и его расширяемая версия iterate). А в других языках реализуют first() и next() для класса и считают, что этого достаточно. Мысль была не эта?

Да.

А можно ссылку?

monk ★★★★★
()
Ответ на: комментарий от monk

Я понял, что в CL нет итераторов, поэтому сделали loop

В CL то как раз есть всякие коллекции с известными размерами, поэтому там foreach довольно легко сделать, и такие функции из коробки. А в С можно узнать размер массива, и то, только если он в текущем файле прописан.

А можно ссылку?

Я сам по быстрому накостылил

#include <stdio.h>

#define over(i, arr, n) if (++i >= n) break;
#define loop for (;;)
#define when(cond) if (!(cond)) continue;
#define with(i) i = -1;

int test_loop(int *arr1, int arr1_len, int *arr2, int arr2_len)
{
        int i, j;

        int sum = 0;
        with(i) with(j) loop {
                over(i, arr1, arr1_len)
                over(j, arr2, arr2_len)
                when(arr1[i] > arr2[j])
                        sum += arr1[i] + arr2[j];
        }
        return sum;
}

int main()
{
        int arr1[] = {1,2,3,4,5};
        int arr2[] = {1,2,3,2,1};
        int d = test_loop(arr1, 5, arr2, 5);
        printf("%d\n", d);
}

MOPKOBKA ★★★★★
()
Ответ на: комментарий от ugoday

Не понял, что ему не нравится. It simply does not suffice to close the file on every temporary process exit and then re-open it on every re-entry to the process. Почему? Чем это отличается от закрытия при каждом окончании функции и переоткрытии при каждом входе в неё? Если он хочет держать файл открытым, можно повесить ответственность на сборщик мусора. Тогда файл будет закрыт, когда исчезнет последняя ссылка, через которую к нему можно получить доступ.

Вообще, чем отличается сохранение продолжения от сохранения замыкания?

(defvar *do*)

(with-open-file (f "file")
  (setf *do* (lambda () (do-something f)))
  
  ...
  
  (funcall *do*)

  ...)

(funcall *do*) ; а здесь та же проблема переоткрытия файла
monk ★★★★★
()
Ответ на: комментарий от MOPKOBKA

В CL то как раз есть всякие коллекции с известными размерами

Чтобы получить размер списка по нему надо пройти, для дерева аналогично. Для хеш-таблицы есть размер, но нет понятия «следующий», есть только maphash.

monk ★★★★★
()
Ответ на: комментарий от bread

Он ставит задачу реализовать пэхапамассивы

А есть ещё языки, где списки и словари реализованы в рамках одной коллекции? Даже в перле это разные типы. Насколько нужно упороться, чтобы сделать как в пхп, мне трудно представить.

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

В lua - соглашение:

local a = { 'a' , [2] = 'b' , [3] = 'c' }

for i = 1, #a do
    print('a[' .. i .. '] = ' .. a[i])
end
print()

for i,v in ipairs(a) do
    print('#' .. i  .. ' = ' .. v)
end
print()

for k,v in pairs(a) do
    print(k .. ' => ' .. v)
end
print()
a[1] = a
a[2] = b
a[3] = c

#1 = a
#2 = b
#3 = c

1 => a
2 => b
3 => c

В перлопитоне специальный синтаксис.

Единственное известное мне исключение из широко распространённых "скриптовых языков" - ruby. Там под объектом Array честный массив.

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

LamerOk ★★★★★
()
Ответ на: комментарий от ugoday

В реальности развитие С идёт по принципу «комитет придумал стандарт → gcc выпустила новую версию».

Наоборот. Тут (на лоре) часто встречается широко распространённое заблуждение, что "комитет придумывает". Комитет как правило не придумывает. Придумывают разработчики компиляторов и стандартных библиотек. А потом предлагают свои, придуманные ими, расширения включить в стандарт.

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

LamerOk ★★★★★
()
Последнее исправление: LamerOk (всего исправлений: 1)
Ответ на: комментарий от LamerOk

Единственное известное мне исключение из широко распространённых «скриптовых языков» - ruby. Там под объектом Array честный массив.

Perl, python, basic, bash.

monk ★★★★★
()
Ответ на: комментарий от LamerOk

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

Разве? Какой компилятор умел вариативные макросы до 1999 года?

monk ★★★★★
()
Ответ на: комментарий от monk

Какой компилятор умел вариативные макросы до 1999 года?

Это коммит, где добавили непосредственно имя предопределённого макро в соответствии с будущим стандартом:

Author: Ulrich Drepper <drepper@cygnus.com>  1998-12-02 00:05:17
...
gcc/cccp.c:
...
 #define REST_EXTENSION_LENGTH	(sizeof (rest_extension) - 1)
 
+/* This is the implicit parameter name when using variable number of
+   parameters for macros using the ISO C 9x extension.  */
+static char va_args_name[] = "__VA_ARGS__";
+#define VA_ARGS_NAME_LENGTH	(sizeof (va_args_name) - 1)
+
 /* The structure of a node in the hash table.  The hash table
    has entries for all tokens defined by #define directives (type T_MACRO),
    plus some special tokens like __LINE__ (these each have their own

Сама же поддержка была добавлена:

Author: Richard Stallman <rms@gnu.org>  1992-05-09 23:37:06
...
gcc/cccp.c:
...
 
+/*
+ * special extension string that can be added to the last macro argument to 
+ * allow it to absorb the "rest" of the arguments when expanded.  Ex:
+ * 		#define wow(a, b...)		process(b, a, b)
+ *		{ wow(1, 2, 3); }	->	{ process( 2, 3, 1,  2, 3); }
+ *		{ wow(one, two); }	->	{ process( two, one,  two); }
+ * if this "rest_arg" is used with the concat token '##' and if it is not
+ * supplied then the token attached to with ## will not be outputed.  Ex:
+ * 		#define wow(a, b...)		process(b ## , a, ## b)
+ *		{ wow(1, 2); }		->	{ process( 2, 1,2); }
+ *		{ wow(one); }		->	{ process( one); {
+ */
+static char rest_extension[] = "...";
+#define REST_EXTENSION_LENGTH	(sizeof (rest_extension) - 1)
LamerOk ★★★★★
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.