«Клубный парадокс» и с ++

37

Я читал статью здесь: http://www.paulgraham.com/avg.html, и часть о "парадоксе пузыря" была особенно интересной. Как человек, который в основном кодирует на c ++, но имеет знакомство с другими языками (в основном Haskell), я знаю о некоторых полезных вещах на этих языках, которые трудно воспроизвести в c ++. Вопрос в основном для тех, кто владеет как c ++, так и другим языком. Существует ли какая-то мощная языковая функция или идиома, которую вы используете в языке, который было бы трудно осмыслить или реализовать, если бы вы писали только на c ++?

В частности, эта цитата привлекла мое внимание:

По индукции, единственные программисты, которые в состоянии увидеть все различия в мощности между различными языками, - это те, кто понимает самый мощный язык. (Это, вероятно, то, что имел в виду Эрик Рэймонд о том, что Lisp делает вас лучшим программистом.) Вы не можете доверять мнению других из-за парадокса Blub: они довольны тем, каким языком они пользуются, потому что он диктует как они думают о программах.

Если окажется, что я эквивалентен программисту "Blub" благодаря использованию c ++, то возникает следующий вопрос: существуют ли какие-либо полезные концепции или приемы, с которыми вы столкнулись в других языках, которые вам было бы трудно осмыслить, если бы вы писал или "думал" на с ++?

Например, парадигма логического программирования, наблюдаемая в таких языках, как Prolog и Mercury, может быть реализована в c ++ с использованием библиотеки Castor, но в конечном итоге я нахожу, что концептуально я думаю в терминах кода Prolog и перехожу на эквивалент c ++ при использовании этого. В качестве способа расширения моих знаний в области программирования я пытаюсь выяснить, есть ли другие подобные примеры полезных / мощных идиом, которые более эффективно выражены в других языках, которые я мог бы не знать как разработчик на С ++. Другим примером, который приходит на ум, является макросистема в lisp, генерирующая программный код внутри программы, по-видимому, имеет много преимуществ для некоторых проблем. Кажется, что это трудно реализовать и думать изнутри c ++.

Этот вопрос не предназначен для дебатов типа "c ++ vs lisp" или каких-либо дебатов типа языковых войн. Задавать вопрос, подобный этому, - это единственный способ, которым я вижу возможность узнать о вещах, о которых я не знаю, о которых я не знаю.

shuttle87
источник
3
См. Также en.wikipedia.org/wiki/Linguistic_relativity
quixoto
2
Я согласен. Пока это не превращается в дебаты между C ++ и Lisp, я думаю, здесь есть чему поучиться.
Jeffythedragonslayer
@MasonWheeler: there are things that other languages can do that Lisp can't- Маловероятно, поскольку Лисп завершен по Тьюрингу. Возможно, вы хотели сказать, что есть некоторые вещи, которые не практичны в Лиспе? Я мог бы сказать то же самое о любом языке программирования.
Роберт Харви
2
@RobertHarvey: «Все языки одинаково мощны в том смысле, что они эквивалентны по Тьюрингу, но это не то слово, которое волнует программистов. (Никто не хочет программировать машину Тьюринга». формально определимо, но один из способов объяснить это - сказать, что это относится к функциям, которые вы можете получить только на менее мощном языке, написав интерпретатор для более мощного языка в нем ». - Пол Грэм, в сноске к обсуждаемому троллпосту. (Понимаете, о чем я?)
Мейсон Уилер
@ Мейсон Уилер: (Не совсем.)
Роберт Харви

Ответы:

16

Ну, так как вы упомянули Haskell:

  1. Образец соответствия. Я считаю, что сопоставление с образцом намного легче читать и писать. Рассмотрите определение карты и подумайте о том, как она будет реализована на языке без сопоставления с образцом.

    map :: (a -> b) -> [a] -> [b]
    map f [] = []
    map f (x:xs) = f x : map f xs
  2. Система типов. Иногда это может быть боль, но это очень полезно. Вы должны запрограммировать это, чтобы действительно понять это и сколько ошибок он ловит. Кроме того, ссылочная прозрачность замечательна. После программирования на Haskell на некоторое время становится очевидным, сколько ошибок вызвано управлением состоянием на императивном языке.

  3. Функциональное программирование в целом. Использование карт и складок вместо итерации. Рекурсия. Речь идет о мышлении на более высоком уровне.

  4. Ленивая оценка. Опять же, речь идет о том, чтобы думать на более высоком уровне и позволить системе управлять оценкой.

  5. Кабала, пакеты и модули. Для меня загрузка пакетов Cabal гораздо удобнее, чем поиск исходного кода, написание make-файла и т. Д. Возможность импортировать только определенные имена намного лучше, чем, по сути, скомпоновать все исходные файлы, а затем скомпилировать.

Джоэл Бургет
источник
2
Замечание о сопоставлении с образцом: я бы не сказал, что его проще писать в целом, но после того, как вы немного прочтете проблему выражений, станет ясно, что такие вещи, как операторы if и switch, перечисления и шаблон наблюдателя, являются низшими реализациями алгебраических типов данных. + Шаблон соответствия. (И давайте даже не будем начинать с того, как Maybe делает исключения нулевого указателя устаревшими)
hugomg
То, что вы говорите, верно, но проблема выражения заключается в ограничении алгебраических типов данных (и о двойных ограничениях стандартного ООП).
Blaisorblade
@hugomg Вы имели в виду шаблон посетителя вместо наблюдателя?
Себастьян Редл
да. Я всегда меняю эти два имени :)
hugomg
@hugomg Речь идет не о том, чтобы иметь Maybe(для C ++ std::optional), а о том, чтобы явно пометить вещи как необязательные / nullable / возможно.
Deduplicator
7

Memoize!

Попробуйте написать это на C ++. Не с C ++ 0x.

Слишком громоздко? Хорошо, попробуйте это с C ++ 0x.

Посмотрите, сможете ли вы превзойти эту 4-строчную (или 5-строчную, что угодно: P) версию во время компиляции в D:

auto memoize(alias Fn, T...)(T args) {
    auto key = tuple(args);                               //Key is all the args
    static typeof(Fn(args))[typeof(key)] cache;           //Hashtable!
    return key in cache ? cache[key] : (cache[key] = Fn(args));
}

Все, что вам нужно сделать для вызова, это что-то вроде:

int fib(int n) { return n > 1 ? memoize!(fib)(n - 1) + memoize!(fib)(n - 2) : 1;}
fib(60);

Вы также можете попробовать что-то подобное в Scheme, хотя это немного медленнее, потому что это происходит во время выполнения и потому что поиск здесь является линейным, а не хешированным (и хорошо, потому что это Scheme):

(define (memoize f)
    (let ((table (list)))
        (lambda args
            (cdr
                (or (assoc args table)
                    (let ((entry (cons args (apply f args))))
                        (set! table (cons entry table))
                        entry))))))
(define (fib n)
        (if (<= n 1)
            1
            (+ (fib (1- n))
                (fib (- n 2)))))))
(set! fib (memoize fib))
Mehrdad
источник
1
Таким образом, вы любите APL, где вы можете написать что-нибудь в одну строку? Размер не имеет значения!
Бо Перссон
@Bo: я не использовал APL. Я не уверен, что вы подразумеваете под "размером не имеет значения", но что-то не так с моим кодом, который заставляет вас так говорить? И есть ли какое-то преимущество в том, как бы вы делали это на другом языке (например, C ++), о котором я не знаю? (Я немного отредактировал имена переменных, на случай, если это то, на что вы ссылались.)
Mehrdad
1
@ Mehrdad - Мой комментарий о самой компактной программе не является признаком лучшего языка программирования. В этом случае APL выиграет руки вниз, потому что вы делаете большинство вещей с одним оператором char. Единственная проблема в том, что это нечитаемо.
Бо Перссон
@Bo: Как я уже сказал, я не рекомендовал APL; Я никогда даже не видел это. Размер был одним из критериев (хотя и значительным, как можно увидеть, если вы попробуете это с C ++) ... но есть ли что-то не так с этим кодом?
Мердад
1
@ Matt: Ваш код memoized на функцию, но этот код может memoize любой функции. Это не совсем эквивалентно. Если вы на самом деле попытаетесь написать такую ​​функцию высшего порядка, как эта, в C ++ 0x, это будет гораздо более утомительно, чем в D (хотя это все еще вполне возможно ... хотя это невозможно в C ++ 03).
Мердад
5

C ++ - это мультипарадигмальный язык, что означает, что он пытается поддерживать множество способов мышления. Иногда функция C ++ является более неловкой или менее беглой, чем реализация на другом языке, как в случае с функциональным программированием.

Тем не менее, я не могу отмахнуться от своей головы о нативной функции языка C ++, которая делает то, что делает yieldв Python или JavaScript.

Другой пример - параллельное программирование . C ++ 0x будет иметь право голоса по этому поводу, но текущий стандарт не имеет, и параллелизм - это совершенно новый способ мышления.

Кроме того, быстрая разработка - даже программирование оболочки - это то, что вы никогда не узнаете, если не покинете область программирования на C ++.

wilhelmtell
источник
Я даже не могу подумать, насколько сложно было бы создать генераторы на C ++ с учетом C ++ 2003. C ++ 2011 сделает это проще, но все же нетривиально. Регулярно работая с C ++, C # и Python, генераторы легко становятся той функцией, которую мне больше всего не хватает в C ++ (теперь, когда в C ++ 2011 добавлены лямбды).
Я знаю, что меня за это застрелят, но если бы мне абсолютно необходимо было реализовать генераторы на C ++, мне пришлось бы использовать ... setjmpи longjmp. Я понятия не имею, сколько это ломает, но я думаю, что исключения будут первыми. Теперь, если вы меня извините, мне нужно перечитать Modern C ++ Design, чтобы выбросить это из головы.
Майк ДеСимон
@ Майк ДеСимоне, не могли бы вы рассказать (кратко) о том, как вы попытаетесь найти решение с помощью setjmp и longjmp?
1
Сопрограммы изоморфны функторам.
GManNickG
1
@Xeo, @Mike: xkcd.com/292
Мердад
5

Сопрограммы - чрезвычайно полезная языковая функция, которая лежит в основе многих более ощутимых преимуществ других языков по сравнению с C ++. Они в основном предоставляют дополнительные стеки, так что функции могут быть прерваны и продолжены, предоставляя языку средства, подобные конвейеру, которые легко передают результаты операций через фильтры для других операций. Это замечательно, и в Ruby я нашел его очень интуитивным и элегантным. Ленивая оценка также связана с этим.

Самоанализ и компиляция / выполнение / оценка кода во время выполнения - все это чрезвычайно мощные функции, которых нет в C ++.

Тони
источник
Сопрограммы доступны во FreeRTOS ( см. Здесь ), который реализован на C. Интересно, что потребуется, чтобы заставить их работать на C ++?
Майк ДеСимон
Совместные подпрограммы - неприятный хак для эмуляции объектов в C. В C ++ объекты используются для связывания кода и данных. Но в Си вы не можете. Таким образом, вы используете стек подпрограммы для хранения данных и функцию подпрограммы для хранения кода.
MSalters
Сопрограммы в C ++: crystalclearsoftware.com/soc/coroutine
Ферруччо,
1
@Ferruccio: Спасибо за ссылку ... в статье Википедии тоже есть несколько. @MSalters: что заставляет вас описывать сопрограммы как "мерзкий взлом"? Кажется, очень произвольная перспектива для меня. Использование стека для хранения состояния также осуществляется рекурсивными алгоритмами - они тоже взломаны? FWIW, сопрограммы и ООП появились на сцене примерно в одно и то же время (в начале 1960-х годов) ... сказать, что первый взлом для объектов в C кажется странным ... Я представляю, что немногие программисты на C тогда интересовались эмуляцией объектов, > 15 лет до C ++.
Тони
4

Внедрив систему компьютерной алгебры как на Лиспе, так и на С ++, я могу вам сказать, что в Лиспе задача была намного проще, хотя я был полным новичком в этом языке. Эта упрощенная природа всех списков упрощает множество алгоритмов. Конечно, версия C ++ была в миллионы раз быстрее. Да, я мог бы сделать версию для lisp быстрее, но код не был бы таким же странным. Сценарии - это еще одна вещь, которая всегда будет проще, например, lisp. Все дело в использовании правильного инструмента для работы.

jeffythedragonslayer
источник
Какая была разница в скорости?
Quant_dev
1
@quant_dev: кратный миллион, конечно!
Мэтт Эллен
Я никогда не измерял это на самом деле, но у меня было ощущение, что большие О были другими. Первоначально я написал версию C ++ в функциональном стиле, и у нее были проблемы со скоростью, пока я не научил ее изменять структуры данных вместо создания новых, измененных. Но это также сделало код труднее для чтения ...
Jeffythedragonslayer
2

Что мы имеем в виду, когда говорим, что один язык «более мощный», чем другой? Когда мы говорим, что язык «выразителен»? Или "богатый?" Я думаю, мы имеем в виду, что язык приобретает силу, когда его поле зрения достаточно сужается, чтобы можно было легко и естественно описать проблему - на самом деле переход состояния, не так ли? - это живет в этом представлении. Тем не менее, этот язык значительно менее мощный, менее выразительный и менее полезный, когда наше поле зрения расширяется.

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

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

И все мое позднее понимание языка более высокого уровня обязано всем моему опыту с ассемблером. Все, что я узнал позже, было легко, потому что, видите ли, все - независимо от того, насколько абстрактно - должно в конце концов приспособиться к аппаратному обеспечению.

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


1 Возможно, не совсем уместно, но carи cdrберут их имена из аппаратного обеспечения: первый Лисп работал на машине, у которой был действительный регистр декремента и реальный адресный регистр. Как насчет этого?

Пит Уилсон
источник
Вы обнаружите, что выбор - это меч с двойным лезвием. Мы все ищем это, но есть темная сторона, и это делает нас несчастными. Лучше иметь очень четкое представление о мире и определенных границах, в которых вы можете оперировать. Люди - очень творческие существа, которые могут делать великие вещи с помощью ограниченных инструментов. Подводя итог, я говорю, что это не язык программирования, а талантливые люди, которые могут заставить любой язык петь!
Чад
«Предлагать - значит создавать; определять - значит разрушать». Думая, что вы могли бы облегчить жизнь с другим языком, вы чувствуете себя хорошо, но как только вы сделаете прыжок, вам придется бороться с бородавками нового языка.
Майк ДеСимон
2
Я бы сказал, что английский намного более мощный и выразительный, чем любой язык программирования, и все же пределы его полезности расширяются с каждым днем, а его полезность огромна. Часть силы и выразительности проистекает из способности общаться на соответствующем уровне абстракции и способности изобретать новые уровни абстракции, когда они встроены.
молбднило
@ Майк, тогда вам придется общаться с предыдущим языком в рамках нового;)
Чад
2

Ассоциативные массивы

Типичный способ обработки данных:

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

Правильный инструмент для этого - ассоциативный массив .

  • Лучшая языковая поддержка для ассоциативных массивов, которые я видел, это MUMPS , где ассоциативные массивы: 1. всегда сортируются 2. они могут быть созданы на диске (так называемая база данных) с тем же синтаксисом. (Побочный эффект: он чрезвычайно мощный как база данных, у программиста есть доступ к нативному btree. Лучшая из когда-либо существовавших систем NoSQL.)
  • Мой второй приз - PHP , мне нравится foreach и простой синтаксис, такой как $ a [] = x или $ a [x] [y] [z] ++ .

Мне не очень нравится синтаксис ассоциативных массивов JavaScript, потому что я не могу создать, скажем, [x] [y] [z] = 8 , сначала я должен создать [x] и a [x] [y] .

Хорошо, в C ++ (и в Java) есть хороший набор контейнерных классов, Map , Multimap , но если я хочу просмотреть, мне нужно сделать итератор, и когда я хочу вставить новый элемент более высокого уровня, я приходится создавать все верхние уровни и т. д. неудобно.

Я не говорю, что в C ++ (и Java) нет пригодных для использования ассоциативных массивов, но несимметричные (или не строго типизированные) языки сценариев превосходят скомпилированные, потому что они являются типизированными языками сценариев.

Отказ от ответственности: я не знаком с C # и другими языками .NET, AFAIK, они имеют хорошую ассоциативную обработку массивов.

ern0
источник
1
Полное раскрытие: MUMPS не для всех. Цитата: Чтобы дать немного более «реальный» пример ужаса, который является MUMPS, начните с участия в одном Международном конкурсе кодов с запутанным кодом C, черты Perl, двух показателей «Фортран» и «СНОБОЛ», а также независимого и несогласованного вклада десятки медицинских исследователей, и вот, пожалуйста.
Майк ДеСимоне
1
В Python вы можете использовать либо встроенный dictтип (например x = {0: 5, 1: "foo", None: 500e3}, обратите внимание, что не требуется, чтобы ключи или значения были одного типа). Попытка сделать что-то подобное a[x][y][z] = 8сложно, потому что язык должен смотреть в будущее, чтобы увидеть, собираетесь ли вы установить значение или создать другой уровень; выражение a[x][y]само по себе не говорит вам.
Майк ДеСимон
MUMPS изначально является языком, похожим на Basic, с ассоциативными массивами (может храниться непосредственно на диске!). Более поздние версии содержат процедурные расширения, что делает его очень похожим на ядро ​​PHP. Один боится Basic и PHP должен найти Mumps пугающим, а другие нет. Программисты этого не делают. И помните, это очень старая система, все странные вещи, такие как однобуквенные инструкции (хотя вы можете использовать полные имена), порядок оценки LR и т. Д., А также не странные решения - имеют только одну цель: оптимизация .
ern0
«Мы должны забыть о малой эффективности, скажем, в 97% случаев: преждевременная оптимизация - корень всего зла. Но мы не должны упускать наши возможности в эти критические 3%». - Дональд Кнут. То, что вы описываете, звучит для меня как унаследованный язык с акцентом на обратную совместимость, и это нормально. Лично в этих приложениях я считаю удобство сопровождения более важным, чем оптимизация, и язык с неалгебраическими выражениями и однобуквенными командами звучит контрпродуктивно. Я обслуживаю клиентов, а не язык.
Майк ДеСимоне,
@Mike: очень интересные ссылки, которые вы разместили, я посмеялся, читая их.
Шаттл87
2

Я не изучаю Java, C \ C ++, Assembly и Java Script. Я использую C ++, чтобы зарабатывать на жизнь.

Хотя я бы сказал, что мне больше нравится программирование на ассемблере и Си. Это в основном связано с императивным программированием.

Я знаю, что парадигмы программирования важны для категоризации типов данных и дают более абстрактные концепции программирования, позволяющие создавать мощные шаблоны проектирования и формализовать код. Хотя в некотором смысле каждая парадигма представляет собой набор шаблонов и коллекций для абстрагирования базового аппаратного уровня, поэтому вам не нужно думать о EAX или IP-адресе внутри компьютера.

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

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

Это становится более сложным, когда вы добавляете парадигмы программирования в микс, потому что все наши взгляды на мир различны.

Чад
источник
2

Есть ли какая-то мощная языковая функция или идиома, которую вы используете в языке, который было бы сложно осмыслить или реализовать, если бы вы писали только на c ++?

Существуют ли какие-либо полезные концепции или приемы, с которыми вы столкнулись на других языках, которые вам было бы трудно осмыслить, если бы вы писали или «думали» на c ++?

C ++ делает многие подходы неразрешимыми. Я бы даже сказал, что большую часть программирования сложно осмыслить, если вы ограничиваете себя C ++. Вот несколько примеров проблем, которые гораздо легче решить способами, которые C ++ делает сложными.

Распределение регистра и соглашения о вызовах

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

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

Генерация кода во время выполнения

Если вы знаете только C ++, вы, вероятно, думаете, что шаблоны - это начало и конец всего метапрограммирования. Это не так. На самом деле, они объективно плохой инструмент для метапрограммирования. Любая программа, которая манипулирует другой программой, является метапрограммой, включая интерпретаторы, компиляторы, системы компьютерной алгебры и средства доказательства теорем. Генерация кода во время выполнения является полезной функцией для этого.

Я рекомендую EVALзапустить реализацию Схемы и поиграть с ней, чтобы узнать о метациркуляционной оценке.

Манипулирование деревьями

Деревья везде в программировании. При разборе у вас есть абстрактные синтаксические деревья. В компиляторах у вас есть IR, которые являются деревьями. В графике и программировании GUI у вас есть деревья сцены.

Этот «смешной простой JSON Parser для C ++» весит всего 484 LOC, что очень мало для C ++. Теперь сравните его с моим собственным простым парсером JSON, который весит всего 60 лок F #. Разница заключается, прежде всего, в том, что алгебраические типы данных ML и сопоставление с образцом (включая активные рисунки) значительно упрощают манипулирование деревьями.

Посмотрите на красно-черные деревья в OCaml .

Чисто функциональные структуры данных

Отсутствие GC в C ++ делает практически невозможным принятие некоторых полезных подходов. Чисто функциональные структуры данных являются одним из таких инструментов.

Например, посмотрите этот 47-строчный сопоставитель регулярных выражений в OCaml. Краткость объясняется в основном широким использованием чисто функциональных структур данных. В частности, использование словарей с ключами, которые установлены. Это действительно трудно сделать в C ++, потому что словари и наборы stdlib являются изменяемыми, но вы не можете изменить ключи словаря или разбить коллекцию.

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

Хвостовые звонки

Мало того, что C ++ не гарантирует хвостовые вызовы, RAII принципиально расходится с этим, потому что деструкторы мешают вызову в хвостовой позиции. Хвостовые вызовы позволяют вам совершать неограниченное количество вызовов функций, используя только ограниченное количество стекового пространства. Это отлично подходит для реализации конечных автоматов, в том числе расширяемых конечных автоматов, и это отличная карта "выхода из тюрьмы" во многих иначе неловких обстоятельствах.

Например, проверьте эту реализацию задачи о ранце 0-1, используя стиль передачи продолжения с памяткой в ​​F # из финансовой индустрии. Когда у вас есть хвостовые вызовы, стиль передачи продолжения может быть очевидным решением, но C ++ делает его неразрешимым.

совпадение

Другой очевидный пример - параллельное программирование. Хотя это вполне возможно в C ++, он чрезвычайно подвержен ошибкам по сравнению с другими инструментами, особенно в том, что касается последовательных процессов, как это видно в таких языках, как Erlang, Scala и F #.

Джон Харроп
источник
1

Это старый вопрос, но так как никто не упомянул об этом, я добавлю список (и теперь буду диктовать) понимания. Легко написать однострочную версию на Haskell или Python, которая решает проблему Fizz-Buzz. Попробуйте сделать это в C ++.

В то время как C ++ сделал огромный шаг к современности с C ++ 11, называть его «современным» языком довольно сложно. C ++ 17 (который еще не выпущен) делает еще больше шагов, чтобы приблизиться к современным стандартам, при условии, что «современный» означает «не из предыдущего тысячелетия».

Даже самые простые из представлений, которые можно написать в Python одной строкой (и соблюдая ограничение длины строки в 79 символов Гвидо), становятся множеством строк кода при переводе на C ++, и некоторые из этих строк кода C ++ довольно запутаны.

Дэвид Хаммен
источник
Обратите внимание: большая часть моего программирования написана на C ++. Мне нравится язык.
Дэвид Хаммен
Я думал, что Предложение Диапазонов должно было решить это? (Даже в C ++ 17, я думаю)
Мартин Ба
2
«массовый переход к современности»: какие «современные» функции предоставляет C ++ 11, изобретенные в текущем тысячелетии?
Джорджио
@MartinBa - Мое понимание предложения "range" заключается в том, что он заменяет итераторы, с которыми легче работать и которые менее подвержены ошибкам. Я не видел никаких предположений, что они позволили бы что-нибудь столь же интересное как списки.
Жюль
2
@ Джорджио - какие особенности любого популярного в настоящее время языка были изобретены в текущем тысячелетии?
Жюль
0

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


Это возможно в Objective-C, и это делает программирование пользовательского интерфейса быстрым. Вы можете сказать кнопке: «Пожалуйста, вызовите этот метод для этого объекта, когда вы нажмете», и кнопка сделает это. Вы можете использовать любое имя метода для обратного вызова, которое вам нравится, оно не зафиксировано в коде библиотеки, вам не нужно наследовать от адаптера, чтобы он работал, и компилятор не хочет разрешать вызов во время компиляции, и, что не менее важно, вы можете сказать двум кнопкам вызывать два разных метода одного и того же объекта.

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

Именно эта особенность Objective-C научила меня ценить способность языка свободно передавать любые типы объектов / функций / все, что важно в концепции языка, а также способность сохранять их для переменные. Любая точка в языке, которая определяет любой тип концепции, но не предоставляет средства для ее хранения (или ссылки на нее) во всех доступных типах переменных, является существенным камнем преткновения и, вероятно, источником многих уродливых, дублированный код. К сожалению, языки программирования в стиле барокко, как правило, имеют ряд таких моментов:

  • В C ++ вы не можете записать тип VLA или сохранить указатель на него. Это эффективно запрещает истинные многомерные массивы динамического размера (которые доступны в C начиная с C99).

  • В C ++ вы не можете записать тип лямбды. Вы не можете даже определить это. Таким образом, нет способа обойти лямбду или сохранить ссылку на нее в объекте. Лямбда-функции можно передавать только в шаблоны.

  • В Фортране вы не можете записать тип списка имен. Просто нет возможности передать список имен какой-либо рутине. Итак, если у вас есть сложный алгоритм, который должен обрабатывать два разных списка имен, вам не повезло. Вы не можете просто написать алгоритм один раз и передать ему соответствующие списки имен.

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

cmaster
источник
1
I have not seen a similarly flexible way to define a callback in any other language yet (though I'd be very interested to hear about them!) То, что вы только что описали, звучит точно так же, как работает управляемый событиями код пользовательского интерфейса в Delphi. (И в .NET WinForms, на которую сильно повлиял Delphi.)
Мейсон Уилер
2
«В C ++ вы не можете записать тип VLA» [...] - в C ++ VLA в стиле C99 не нужны, потому что мы имеем std::vector. Хотя он немного менее эффективен из-за того, что не использует выделение стека, он функционально изоморфен VLA, поэтому на самом деле не считается проблемой типа «blub»: программисты на C ++ могут посмотреть, как это работает, и просто сказать: «ах, да , C делает это более эффективно, чем C ++ ".
Жюль
2
«В C ++ вы не можете записать лямбда-тип. Вы даже не можете его определить. Поэтому нет способа обойти лямбду или сохранить ссылку на нее в объекте» - вот для чего std::function.
Жюль
3
«Я еще не видел такого же гибкого способа определения обратного вызова на каком-либо другом языке (хотя мне было бы очень интересно услышать о них!).» - на Java вы можете написать, object::methodи он будет преобразован в экземпляр любого интерфейса, который ожидает принимающий код. C # имеет делегатов. У каждого объектно-функционального языка есть эта особенность, потому что это в основном точка сечения двух парадигм.
Жюль
@Jules Ваши аргументы - это как раз то, о чем говорит Blub-Paradox: будучи опытным программистом на C ++, вы не рассматриваете их как ограничения. Тем не менее, они являются ограничениями, и другие языки, такие как C99, являются более мощными в этих конкретных точках. К вашему последнему пункту: во многих языках возможны обходные пути, но я не знаю ни одного, который действительно позволял бы вам передавать имя какого-либо метода другому классу и вызывать его для некоторого объекта, который вы также предоставляете.
Cmaster