Блоґ одного кібера

Історія хвороби контуженого інформаційним вибухом

Пошук гамільтонового циклу на CLISP

with 44 comments

Лабораторна яку я ще не здав. Бо Володимира Юрійовича і без мене було кому діставати. Але сидів до першої ночі, а потім від шостої ранку до першої пари. Потім дві години ходив за Володимиром Юрійовичем. 🙂 Безрезультатно. Щоденник закінчую, далі обгрунтування актуальності:

Лісп, бо він простий і елегантний, на відміну від Пролога. Ну, і звісно Лісп може бути імперативним, тобто іти на компроміс з вашими звичками. Пролог такого не допустить, або я ще не вияснив як.

І що найголовніше Лісп, хоча це й мова на якій Бог написав всесвіт, достатньо проста. Принаймі простіша за C++. І дуже дуже приємна. Натикаєшся наприклад на функцію (dolist (x list)..., і думаєш, хей, це ж як for x in list: .... А (make-array (list FIELD_SIZE FIELD_SIZE) :initial-element 0) – це майже MatLab. Про lambda, map, reduce, eval я взагалі мовчу.

Крім того, синтаксис простіший ніж в XML.

Найбільша проблема в вивченні Lisp – це визначити що вчити – його, чи Scheme. Схему я ще не копав достатньо глибоко, щоб розповідати про відмінності. Знаю лише що до плюсів Scheme входить SICP. А ще функції описуються за допомогою (define, а не (defun, і в цьому напевне є якась змістовна відмінність.

А до плюсів Common Lisp я сміливо відношу те, що з ним можна познайомитись за кілька годин, дуже весело, і без шкоди для здоров’я. Тут.

Ах, ну і власне, лабораторна:

Умова

КиГ №22 4г – вказівники.
Ми стартуємо у верхній лівій клітинці, яку позначено цифрою 1. Дозволено рухатись тільки в напрямку вказаному стрілками, тобто з першої можна перейти в будь-яку з тих що під нею. Наше завдання – попасти в кожну з клітинок поля послідовно їх нумеруючи, і закінчити в останній, позначеній телефоном, і цифрою 25. Коли всі цифри пронумеровані – знайти їх суму в середньому рядку.

Ах. І ще одне. Клітинка позначена цифрою 17 має бути позначена так само і у вас. Інакше може вийти відповідь не 66, а 60 наприклад, хоча цикл все одно буде гамільтоновим.

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

Код

; №22 4г – вказівники
; Починаємо в верхній лівій клітинці, а далі можемо рухатись лише в напрямку вказаному стрілкою.
; Де зупинитись – вибираємо самі. треба прийти в нижню праву клітинку. 

; Напрямки: 
; 7 8 9 
; 4   6
; 1 2 3

; поле:
(defconstant FIELD_SIZE 5)
(defconstant FIELD_SIZE-1 4)
(defconstant CELL_COUNT 25)

(setf field #2A((2 2 1 4 1) 
                (6 3 3 3 1) 
                (2 8 8 4 1) 
                (9 9 7 1 8) 
                (6 9 4 8 0)))

(defun nl () "newline"
        (format t "~%"))

(defun printfield (&optional (what field)) ; print field (by default) or any other 2d array
        (dotimes (y FIELD_SIZE) 
                (progn 
                (dotimes (x FIELD_SIZE)
                        (format t "~2d " (aref what y x))
                )
                (nl))))

(setf visits (make-array (list FIELD_SIZE FIELD_SIZE) :initial-element 0))

(defun infield (x y) (and (>= x 0) (>= y 0) (< x FIELD_SIZE) (< y FIELD_SIZE)))

(defun waylist (x y dir)
        (if (not (infield x y))
                nil
                (cond ((= dir 1) 
                        (cons (list x y) (waylist ( x 1) (+ y 1) dir)))
                ((= dir 2) 
                        (cons (list x y) (waylist x (+ y 1) dir)))
                ((= dir 3) 
                        (cons (list x y) (waylist (+ x 1) (+ y 1) dir)))
                ((= dir 4) 
                        (cons (list x y) (waylist ( x 1) y dir)))
                ((= dir 6) 
                        (cons (list x y) (waylist (+ x 1) y dir)))
                ((= dir 7) 
                        (cons (list x y) (waylist ( x 1) ( y 1) dir)))
                ((= dir 8) 
                        (cons (list x y) (waylist x ( y 1) dir)))
                ((= dir 9) 
                        (cons (list x y) (waylist (+ x 1) ( y 1) dir)))
                ((= dir 0) nil)
                )))

(defun canvisit (x y)
        (and (infield x y) (= (aref visits y x) 0))
)
(defun visit (x y n) ; return true if going to cell x y in n-th route will lead you to finish
        (defun vis (x y) (setf (aref visits y x) n))
        (defun unvis (x y) (setf (aref visits y x) 0))
        (if (canvisit x y) 
                (progn 
                (if (and (= x 2) (= y 3) (not (= n 17))) (return-from visit nil)) ; special condition
                (vis x y)
                (if (and (= x FIELD_SIZE-1) (= y FIELD_SIZE-1) (= n CELL_COUNT)) 
                        T
                        (progn
                        (dolist (point (cdr (waylist x y (aref field y x))))
                                (if (visit (first point) (second point) (1+ n)) 
                                        (return-from visit T)))
                        (unvis x y)     
                        nil
                        )))
                nil)
)

(printfield)
(print "Wait for it!")
(print (visit 0 0 1)) (nl)
(printfield visits)
(setf res 0)
(dotimes (x FIELD_SIZE) (setf res (+ res (aref visits x 2))))
(print res)

Тест:

 2  2  1  4  1 
 6  3  3  3  1 
 2  8  8  4  1 
 9  9  7  1  8 
 6  9  4  8  0 

"Wait for it!" 
T 
 1  4  8  3  6 
18 24 19 12 21 
 9 23  7 22 13 
 2  5 17 14 20 
10 16 15 11 25 

66 
Натисніть ENTER або введіть команду для продовження

Written by bunyk

31 Травня, 2011 at 15:06

Опубліковано в Кодерство, Конспекти

Tagged with

Відповідей: 44

Subscribe to comments with RSS.

  1. “Простота” і “елегантність” ліспа у даному випадку ховаються за імперативним баченням світу програмістом. Краще написав би уже на пітоні. Тобі “просто” було писати префіксні умови? =)

    danbst

    31 Травня, 2011 at 22:55

    • Не можна Пітон. Лісп або Пролог. Довелось обирати менше з двох збочень. 😀

      І не бачу нічого поганого в імперативному баченні світу. Комп’ютери імперативні. Вони мають стан. І змінюють його. Хоча якщо ти такий крутий лямбда-самець – покажи мені як це можна було переписати функціонально.

      А що таке префіксні умови?

      bunyk

      31 Травня, 2011 at 23:11

    • Я мав на увазі ось це:
      (and (infield x y) (= (aref visits y x) 0))

      На рахунок лямбда-самець, згоден, жарт цікавий. Переписати треба не функціонально, а по-ЛІСПовському. Тобто так, щоб ніхто окрім лісперів не розумів що тут стається. Для цього потрібно використати:
      побільше car cdr cons
      defmacro ОБОВ’ЯЗКОВО!!!!
      ну і використати якісь попсові функції вищого порядку, хоча це не суть

      На щастя, я не знаю ЛІСП, тому і переписати не зможу..

      danbst

      1 Червня, 2011 at 00:44

      • (cons є, а замість car написано (first, замість (car (cdr – (second.

        Ну, і не ясно чому писати префіксні умови має бути складно, якщо тут геть усе префіксне. Мене правда трохи дістало писати (- FIELD_SIZE 1), то я завів собі константу FIELD_SIZE-1 (крута назва для константи, правда? Інші мови таку назву не дозволять).

        bunyk

        1 Червня, 2011 at 11:51

      • Forth дозволить таку назву =) Скоро напишу ще один пост про Форт-ОС, благо уже майже зробив.

        Загальний зміст того, що я хотів сказати – круто, що ти пишеш на Ліспі, але погано, що до тебе досі не прийшло enlightenment, 5 абзац

        danbst

        1 Червня, 2011 at 12:44

        • Я так і подумав, що хороший значить великий. 🙂

          bunyk

          2 Червня, 2011 at 10:01

      • звідки стільки негативу?)

        dmytrish

        9 Червня, 2011 at 20:34

      • Якщо задовбує писати префіксні умови, можна написати свій транслятор інфіксних у префіксні або узяти мій тут: http://dmytrish.selfip.net:81/repo/?view=mexp.lisp

        dmytrish

        11 Червня, 2011 at 16:49

        • Хоч і перетворення Lisp’ів в префіксну мову часто приводять як приклад потужності макросистеми, завжди дивився на “писунів інфікса” з недовірою. Ламати семантичну модель заради можливості написання чотирьох математичних операцій і двох логічних – просто безглуздо.
          Тим більше, якщо просте вирівнюванням коду (alignment, а не identation, важливо) робить префіксні умову в рази зромумілішими, ніж інфіксні: не потрібно шукати дужки в умові, деревоподібний вигляд робить це за тебе.

          alex.yakushev

          11 Червня, 2011 at 23:35

          • «ламати семантичну модель» — а хто її ламає-то? Для мене семантична модель — це те, що створюється після розкриття усіх макросів і інтерпретації того, що вийшло. У моєму випадку змінюється тільки синтаксис (що дуже типово для ліспа і чим усі ліспери хваляться), семантика після розкриття мого макросу буде та сама, якби я писав дужечки ^_^

            Щодо недовіри до «писунів інфікса» — все одно кожен ліспер рано чи пізно починає винаходити велосипеди, сама структура мови, в якої немає синтаксису, підводить до цього — якщо синтаксису як такого немає, його слід вигадати. І для найпростішої задачки доводиться вигадувати свій синтаксис. В цьому плані Лісп мені нагадує асемблер структурного програмування — він так само не прикритий синтаксичністю, все, що є, це голі атомарні структури, в яких програміст (принаймні новачок у Ліспі, такий, як я) плутаєтсья дуже легко, приблизно так, як у асемблерному коді (а тут я уже не новачок і знаю, що кажу): можливості безмежні, контроль (над AST у випадку Ліспа і машинним кодом у випадку асемблера) всеосяжний, от тільки починати програмувати доводиться з того, що поверх і того, і того наворочувати свій синтаксис і, по суті, свлю мову. Називати Лісп мовою програмування — це приблизно те саме, що називати нею асемблер. І якщо говорити про синтаксис, то можна провести дуже промовисту паралель між діалектами асемблера і діалектами Ліспа.
            Єдине, що у Ліспа дійсно неймовірно потужні засоби до самоорганізації і надбудовування, і якщо взяти якусь конкретну реалізацію, то там уже з’являється зародковий синтаксис, хоч-не-хоч

            dmytrish

            12 Червня, 2011 at 17:54

            • Будь-яка макра порушує цю семантику, тому кожна макра повинна бути виправдана:
              – кращим розумінням программи при її використанні, або
              – скороченням кількості повторного коду.
              Введення інфіксної форми явно не відповідає другому пункту. Для мене не відповідає і першому.
              Звідси-ж і недовіра до лісперів-любителей інфікса. Якщо вам не подобається префіксна форма, пишіть на цепепе.

              В стіні тексту про Асемблер і Лісп все більш-менш вірно, крім:
              >>Називати Лісп мовою програмування — це приблизно те саме, що називати нею асемблер.
              Асемблер – це набір мнемонічних кодів для конкретної архітектури. Лісп – високорівнева мова програмування, за допомогою якої можна створити структури програмування будь-якого рівня абстракції. Як можна взагалі порівнювати такич чином ці дві інструменти?

              alex.yakushev

              13 Червня, 2011 at 16:17

              • Коли я говорив про подібність препроцесора Сі і макросів, я мав на увазі, що і той, і другий оперують без знання семантики програми. Темплейти ж завдають собі клопоту залізти в типи і подивитись, чи все нормально. За кучею інших параметрів макросистема потужніше і повніше, да.

                Так само я говорю про подібність засобів Ліспа і асемблерів: їх атомарні структури занадто атомарні для нормальних задач у обох мов, хоч і по-різному, у Ліспа вони структурні і самоподібні, що дозволяє його ж засобами будувати щось будь-якої складності (хоч, з другого боку, мені як людині важко не бачити з першого погляду різниці між рівнями абстракції: прірва між (cons a b) і (join first-neural-network second-neural-network) вимагає вникнення у суть програми, тоді як звичайні мови примушені використовувати свою мову для кожного рівня).

                Дякую за приємний флейм.

                dmytrish

                14 Червня, 2011 at 16:52

  2. Код жахливо неідеологічний. В принципі, CL має свої проблеми з зручним використанням структур данних, але в цій програмі річ не в них.
    Імперативний код тут не потрібен зовсім. Рахувати суму масиву через mutable variable – взагалі злочин над людством.
    Ось як це повинно виглядати на clojure. Переписав з максимальним збереженням реалізації самого алгоритму: http://pastebin.com/r0B2d2HM

    danbst: казки про те, що лісповий код складається з одних cons, car і cdr, придумують самі жалюгідні цепепе-джавісти, в яких без оператора присвоювання зникає потенція. Не вір їм;)

    alex.yakushev

    2 Червня, 2011 at 01:06

    • Я їх і не слухаю, я займаюсь троллінгом ) Все-рівно, корисного нічого по коду не скажу.

      danbst

      2 Червня, 2011 at 07:42

  3. Поспішив, не те скопіював з repl’а.
    http://pastebin.com/frpDMqb7

    alex.yakushev

    2 Червня, 2011 at 01:10

    • Переписав суму масиву функціонально.

      Було:

      (setf res 0)
      (dotimes (x FIELD_SIZE) (setf res (+ res (aref visits x 2))))
      (print res)

      Стало:

      (print (reduce #’+ (mapcar (lambda (x) (aref visits x 2)) (0 1 2 3 4))))

      Не розумію як (reduce + (result 2)) вийшло таким коротким…

      bunyk

      2 Червня, 2011 at 06:38

      • В clojure вектор також є функцією від номера елементу, яка повертає цей елемент. Т.ч. (result 2) – дістати 3 елемент з вектору result, тобто третій рядок.

        alex.yakushev

        2 Червня, 2011 at 14:57

  4. гм, важко сказати, що краще — Scheme чи CLisp.

    Common Lisp — неприємна річ. Жирний, архаїчний, безладно імперативно-функціонально-об’єктно-орієнтований. Його хвалені макроси програють навіть метапрограмуванню в С++ (і відповідвають потужному препроцесору для С). Для нього, в принципі, є багато бібліотек, але більшість із них закинуті/в них чогось не вистачає/вони далеко неідеальні/немає документації. Наприклад, коли мені знадобились регулярні вирази, я покопався в репах і поставив cl-regex. Коли після першого wtf?! я сходив у #lisp, мені там сказали, що ніхто не користуєтсья cl-regex, мені треба ставити cl-ppcre, якого не було в репах. Синтаксис CLisp часто непослідовний і переповнений синтаксичним цукром, важчий для розуміння, ніж у Scheme (CLIsp дозволяє назвати клас, функцію і змінну одним і тим же іменем, що мене реально заплутувало; імена чистих і імперативних функцій безладно перемішані, я не скажу без довідника, яка з функцій delete чи remove чиста, тоді як у Схемі однозначно буде delete і delete!). Його об’єктна система, CLOS, потужна до кошмарності, в ній можна робити навіть більше непотрібних речей, ніж у С++ (типу передметодів, післяметодів і приоритизації запитів, хоч загалом ідея узагальнених функцій дуже непогана). Але, попри всі його вади, він більш придатний для повсякденного життя. ніж Scheme.

    Scheme елегантна, логічна, проста, послідовна — вона шедевральний хак, але, наскільки я знаю, в ній важче із кодовою базою, менше хоч трохи якісних бібліотек, ще більша фрагментація (але тут я не експерт), цей рай для математика, але я не знаю, наскільки добре там програмісту.

    dmytrish

    9 Червня, 2011 at 20:50

    • Обожнюю такі коментарі.

      А Якушев напевне сказав би що краще Clojure.

      bunyk

      9 Червня, 2011 at 22:04

      • A Closure нічо так на перший погляд, треба буде помацати, дякую, не знав

        dmytrish

        10 Червня, 2011 at 16:22

    • >>Його хвалені макроси програють навіть метапрограмуванню в С++ (і відповідвають потужному препроцесору для С)
      Кори наївся? Препроцессор в С – до неможливості убогий і збоченний спосіб перестріляти собі всі дві ноги. Метапрограмування в Цепепе – історично безглузде нагромодження костилів і воркераундів, за яке варто розстрілювати до народження (батьків, себто).
      З іншим в цілому згоден – CL в першу чергу індустріальна мова, тому особливо не блистає елегантністю і специфікація переповнена через край.
      Схема – мова для навчання. Крапка. Хоч і спека R6RS досить потужна, спроба написати на Scheme щось серйозне – це переписування сотень велосипедів.
      Якушев каже, що Clojure – не кращий з ліспів, але чудово підходящий для real-world програмування. Хоча б тому, що може використовувати весь Java code base без будь-яких проблем.

      alex.yakushev

      10 Червня, 2011 at 23:50

      • гг, препроцесор в Сі не настільки вже й убогий. Наприклад, коди ядра лінукса переповнені препроцесорними директивами, макросми типу for_each_cpu, list_for_each, і я б не сказав, що це створює проблеми, навпаки, при вмілому використанні це прекрасна штука. Я б порівняв його із рибою фугу — вона отруйна, але при вмілому приготуванні це хороший делікатес.
        І приблизно ті самі відчуття у мене викликають макроси ліспа, особливо у варіанті CL: просто брудні маніпуляції над AST без жодного зважання на типи і безпечність таких перетворень (Схема принаймні завдала собі клопоту ввести ще й гігієнічні макроси, і «негігієнічність» — це влучне слово для опису макросів CL). Навіщо всі ці збочення? Щоб у рантаймі ловити наслідки протікання абстракцій?!

        Щодо метапрограмування в С++ не треба. Строгі перевірки типів, повнота по Тьюрингу, практично функціональна мова для кодогенерації, тісно переплетена з типовою системою С++, можливість статично відтворювати багато штук, які робляться в динамічних мовах (і все під час компіляції, з прекрасною оптимізацією) — можливо, це все не придатне до індустріального використання, але назвати це костилем у мене язик слабо повертається.

        dmytrish

        11 Червня, 2011 at 04:59

        • Препроцессор в С – це не риба фуга. Це роздовбана гвинтівка, стріляюча урановими медведями. Шанс 1 зі 100 що ти вистрілиш і в щось навіть попадеш. 99 із 100 – що вона рване прямо в тебе в руках.
          >>просто брудні маніпуляції над AST
          Orly? А по моєму, пряма маніпуляція над AST – найпотужніше, що можна придумати. Особливо коли в тебе є сама мова як засіб маніпуляції над цим деревом. Так, макри в CL низькорівневіші, ніж в Схемі (яка, нагадаю, створювалась як навчальна мова) – і тим потужніші. Сішний препроцесор – це операції над паршивими строками, тут про безпеку взагалі ніякої розмови йти не може. Ось один з прикладів: http://hbfs.wordpress.com/2009/11/17/defines-are-evil/
          Яка різниця ловити помилки розвертання макросів в ріалтаймі чи при компіляції? Ти що, свій код не дебажиш? Як тільки скомпілилось – випускаєш в реліз? Та якщо б ще кожній мові такий рантайм, як в CL, я б всюди помилки в рантаймі ловив.

          >>Щодо метапрограмування в С++ не треба. Строгі перевірки типів, повнота по Тьюрингу, практично функціональна мова для кодогенерації, тісно переплетена з типовою системою С++, можливість статично відтворювати багато штук, які робляться в динамічних мовах (і все під час компіляції, з прекрасною оптимізацією).
          З обкладинки книги Александреску прочитав? Х**ня ваша тюрінг-повнота. Х**ня ваша кодогенерація. Темплейти і Цепепе – дві абсолютно різні мови. Це як вбудувати парсер-генератор в компілятор самої мови. Насправді так воно і є. Ти не можеш використовувати ніяких можливостей цепепе при написанні темплейтів. Ти повинен постійно перемикати свій мозок між семантиками цих двох абсолютно різних мов. Про сотні помилок, які вивалює компілятор, коли ти допустив в одному-єдиному темплейті помилку і як легко її знайти – взагалі говорити не хочеться.
          До недавнього я бачив тільки одну нішу для цепепе – складні MPI-ні розподілені обчислення, які складно описати на чистому С (хоча ситуація теж досить міфічна). Тепер, коли Hadoop представляє собою досить серйозну річ, необхідність цепепе прямує до нуля.

          alex.yakushev

          11 Червня, 2011 at 11:32

          • З чим я згодний:
            1) да, лісп строго типізований, і динамічність пом’якшує наслідки вивалювання в рантаймі
            2) «пряма маніпуляція над AST – найпотужніше, що можна придумати. » — да, однозначно. З тим тільки застереженням, що іноді потужність грає проти програміста.
            3) «Сішний препроцесор – це операції над паршивими строками» — а, по-моєму, в силу ще більшої загальності, це ще потужніше, ніж маніпуляція над уже прожованими токенами (trollface)
            4) да, я знаю, що темплейти і плюси — дві мови з різних світів. І да, Лісп в цьому плані можливо виграє.
            Про складність знаходження помилок в плюсах охоче вірю, тільки і ліспівські кондішни рідко захоплюють своєю інформативністю (можливо, у мене просто малий досвід, я не писав чогось серйозного на CLisp).

            dmytrish

            11 Червня, 2011 at 15:36

            • Ти не зрозумів про що я.
              В Lisp’ах немає синтаксису – тільки чисте AST. Макросистема Lisp’ів надає повний доступ до чистого AST. Ефективність – безмежна.
              В С/C++ – конекстно-залежна граматика і жахливий синтаксис (відноситься більше до С++ все таки). Препроцесор працює всього лиш на рівні строк. Ефективність – практично нульова.
              В Clojure з беспечним захопленням символів можна написати об’ємний складний макрос і бути впевненим, що він спрацює правильно в будь-якому випадку. Використання в С дефайна більшого за однострочник нагадує гру в російську рулетку.

              alex.yakushev

              11 Червня, 2011 at 17:41

              • Приклад _великих_ макросів препроцесора:
                http://lxr.free-electrons.com/source/include/math-emu/op-common.h
                Макрос _FP_PACK_CANONICAL завждовжки 114 рядків використовується 19 разів,
                макрос _FP_DIV завдовжки 61 рядок — 102 рази
                макрос _FP_SQRT у 47 рядків — 37 разів
                макрос _FP_MUL у 55 рядків — 122 рази

                І, по-моєму, ще ніхто не закидав лінуксам те, що у них софтверна математика з плаваючою комою нагадує гру в російську рулетку.

                Це все до того, що я не люблю емоційних перебільшень, як про російську рулетку.

                dmytrish

                14 Червня, 2011 at 18:11

                • Oh, come on. Декілька коментарів назад ти мені розповідав про невимовну потужність сішного препроцесора, а зараз показуєш мені імітацію інлайнових функцій?
                  Покажи мені реалізацію банального if-it(exp) (який працює таким чином: якщо умова exp справджується, то макро біндить значення exp до змінної it, яку надалі можна використовувати в наданому макросу тілі). І щоб це макро можна було використовувати вкладеним одне в друге. Нехай навіть exp може бути тільки типу int.
                  По моїм прогнозам, ти прийдеш до макросу, який доведеться закривати двома фігурними дужками замість одної. Чому? Тому що препроцесор не може навіть банально вставити тіло, яке ти передаєш в макрос, в фігурні дужки. Ти взгалі не можеш передати шматок коду в макрос (не кажучи вже про його модифікацію). Все що ти можеш – це вбудованим Search\Replace’ом замінити в компайл-таймі одну строку на декілька інших. Де обіцяні суперможливості?
                  Звичайно, це макро можна переписати під стиль if-let, де ти сам називаєш змінні. В ліспах теж так роблять. Я привів приклад найпростішого, чого С нормально зробити не може. Інше якщо хочеш я можу розписати по пунктам. Отже, чому C-шний препроцесор не може вважатись повноцінним інструментом для метапрограмування:
                  – відсутній спосіб згенерувати безпечні локальні змінні (gensym)
                  – неможливо здійснити операцію напряму над кодом. Наприклад, як на препроцесорі реалізувати макро switch для довільної функції порівняння (як я зробив тут http://unlog1c.livejournal.com/22231.html).
                  Мушу бігти, потім може ще допишу:)

                  alex.yakushev

                  15 Червня, 2011 at 08:25

                  • #define if_it(exp, type) \
                    for ( type it = (exp), *once = (void *)1; once; once = (void *)0) \
                    if (it)

                    #include

                    int main() {
                    if_it(1, int) {
                    printf(“%d”, it); // виводить 1

                    if_it(2, int)
                    printf(“%d”, it); // виводить 2
                    }
                    }

                    (компілюється як gcc -std=c99)

                    Да, в сішному препроцесорі немає ніяких суперможливостей, взагалі я нікому не збирався доводити, що сішні макроси панацея і що вони для придатні для всього. І тому мені не особливо приємно чути, що в CL вони панацея. І взагалі, вимагати написати на Сі щось ліспоподібне — не найкраща ідея.

                    1) відсутній спосіб згенерувати безпечні локальні змінні (gensym) — а кому це взагалі може бути потрібно на Сі?
                    2) для довільної функції порівняння на Сі я напишу функцію, а не макрос (як це зроблено в qsort() із стандартної бібліотеки).

                    Я просто хотів сказати, що цей іструмент має право на існування і в рамках своїх задач працює (і навіть почав збочуватись і підлаштовуватись під вимоги робити з Сі Лісп)

                    dmytrish

                    16 Червня, 2011 at 00:48

                    • Хороший workaround – поставити об’явлення змінної в холостий for, я до цього сам не додумався:).
                      Звичайно, я не хочу сказати, що Сішні макри зовсім безнадійні. В кінці кінців, люди пишуть на них щось і це щось працює. Мій власний досвід спотикання об препроцесор – це розставлення дужок навкого кожного аргумента, а потім ще навколо операцій над аргументами і т.д. Насправді, в дужках немає нічого поганого, але в самому С лишні дужки неідеологічні. Як результат – знову ж таки різниця в стилі між написанням дефайнів і написанням основного коду.
                      Можливо, я після ліспоподібних мов занадто розбалуваний гомоіконністю і гомогенністю щоб сприймати це нормально. Інші, як я бачу, сприймають і нормально використовують.
                      Я ж використовую дефайни тільки коли підключаю потрібні бібліотеки для AVR-них контролерів. Це виглядає досить безпечно і стабільно. У всьому іншому я і препроцесор живем в різних вимірах.

                      alex.yakushev

                      16 Червня, 2011 at 01:20

          • Ну ти й розійшовся.

            >>> До недавнього я бачив тільки одну нішу для цепепе – складні MPI-ні розподілені обчислення, які складно описати на чистому С (хоча ситуація теж досить міфічна).
            А FPS на чому писати? Теж на Джаві?

            bunyk

            11 Червня, 2011 at 15:40

            • Ігрову логіку уже давно не пишуть на цепепе. Для цього є Lua, колись був Tcl. Ігрові двигуни чудово пишуться на чистому С.
              С – прекрасна нішева мова для низькорівневого системного програмування та embedded. С++ – еволюційна помилка.

              alex.yakushev

              11 Червня, 2011 at 17:46

          • Тепер конструктивніше: мені незрозуміло, чому ти одночасно так підносиш до небес свободу, яку дає Лісп, і так затято обливаєш гівном плюси (да, мова далеко не ідеальна), які також дають свободу робити багато всяких речей (в тому числі і неправильних). По-моєму, свобода в будь-якій мові не звільняє від відповідальності за наслідки своїх дій.

            Да, щось курив я ліспівсьску траву, от тільки не вставляє (поки що?), тільки кашляти хочеться. Буду курити далі, можливо, вставить.

            dmytrish

            11 Червня, 2011 at 16:43

            • 1. Відповів вище. Не варто плутати свободу з даванням підпаленого динаміту сліпій дитині. Лісп надає ЗРУЧНИЙ і ЕФЕКТИВНИЙ інструмент з свободою дій і наслідками на тобі. Препроцессор С надає НЕПЕРЕДБАЧУВАНИЙ і НЕБЕЗПЕЧНИЙ інструмент з свободою дій і наслідками на тобі. По змісту вони може і схожі, але відрізняються мінімум на порядок.

              alex.yakushev

              11 Червня, 2011 at 17:51

              • Це не відповідь, це повторення мантри про те, що «Лісп … ЗРУЧНИЙ і ЕФЕКТИВНИЙ», а «С … НЕПЕРЕДБАЧУВАНИЙ і НЕБЕЗПЕЧНИЙ». Технічніше і конкретніше, будь ласка. Без мантр.

                dmytrish

                12 Червня, 2011 at 18:02

                • Мантри? Ні, дякую, я атеіст.
                  Я привів аргументи. Я привів посилання. Якщо цього недостатньо, нічим не можу допомогти. Пояснення для сліпих і глухих ще в процесі розробки.

                  alex.yakushev

                  13 Червня, 2011 at 16:30

        • Гігієнічні макроси — ІМНО, повна туфта (інакше чому ніде, крім схеми, їх не намагалися впровадити?). По-перше, це взагалі не лісп-стайл, де головною фішкою є код як список даних і можливість обробляти цей код програмно. Викидаємо з ліспу дефмакро — отримуємо щось неповоротке на рівні С/С++. Звичайно, евал нікуди при цьому не зник, але то вже не те задоволення (втім, у багатьох реалізаціях схеми дефмакро теж провсяк випадок залишили). По-друге, синтаксис їх зробили занадто переобтяженим зайвими деталями — дефмакро виглядає набагато простішим і очевиднішим у порівнянні з ними. Що ж стосується гігієни, по-моєму, для більшості випадків тут цілком досить вбудувати в макрос генерацію імен (гарно це зробили в клоджері, до речі).

          Python

          17 Вересня, 2011 at 21:57

  5. Я взагалі дивуюсь, чому мої найдибільніші пости зазвичай викликають найдовші обговорення. 🙂

    bunyk

    14 Червня, 2011 at 17:23

  6. […] замість того щоб пояснити що там могло відбуватись сказали вивчити Lisp або Prolog. І напевне не змогли б пояснити, бо аби це зрозуміти […]


Залишити відповідь на dmytrish Скасувати відповідь

Цей сайт використовує Akismet для зменшення спаму. Дізнайтеся, як обробляються ваші дані коментарів.