Pull to refresh
4
0

Разработчик ПО

Send message

Проверку типов реализовать несложно:


abstract sealed class Expr[+T]
object EmptyExpr extends Expr[Unit]
case class If[+R,R1:R,R2:R](test: Expr[Boolean],ifTrue: Expr[R],ifFalse: Expr[R] = EmptyExpr) extends Expr[R]

Язык арифметических выражений придуман не очень хороший, отсюда "прагматические недостатки" 2-4.


Синтаксисы let и where как-то дублируют друг друга.


Кажется, из-за вложенности классов допустимо что-то вроде Result.Some.Some.Error("...") (не уверен). Это было бы странно для DSL.


Без приоритета операций совсем никуда — нехорошо, если выражение 2 + 1 * 3 внезапно равно 9.


Нагляднее было бы представлять выражения в виде деревьев, тогда могло бы появиться "описание грамматики" (в виде case-классов, как и у вас), приоритет операций стал бы не нужен, и наверно можно было бы ввести какую-то типизацию.


Пример:
val expr = If(
  Less(
    Var("x"),
    Const(3)),
  Mul(Var("y"),Const(4)), 
  Mul(
    Const(5),
    Add(
      Const(1),
      Const(2)))) where (
  "x" eq 43,
  "y" eq 3)

Здесь синтаксический сахар языка Scala использован не в полной мере, т.к. я не знаю, что из сахара есть в Kotlin. Это — минималистичный по использованию сахара вариант. Можно писать и короче, типа Mul (var"x", var"y", 5, 6)

a[i] и *(a+i) эквивалентны, и поэтому компилируются абсолютно одинаково.

Спасибо за статью.


x86-64 процессор имеет 16 регистров для целых чисел и 16 YMM регистров для чисел с плавающей точкой.

Читал, что в суперскалярных процессорах (по крайней мере, начиная с Pentium) число физических регистров существенно превышает число архитектурных (физических несколько сотен, архитектурных 16). Почему ухудшение производительности у вас проявлялось при 20 аккумуляторах? Можете нащупать границу более точно? Возможно, производительность ограничивалась чем-то другим?

А почему не на макросах? case классы — это же элементарно и совершенно очевидно.
Некоторые даже на С++ шаблонах дифференцирование делают.

Не сразу заметил, что под временем доступа O(√N) имеется в виду среднее время доступа к объёму памяти N (именно это, а не что-то другое, замеряет автор своими тестами), а не худшее время одиночного чтения.


В рамках этой статьи мы O(f(N)) будет означать, что f(N) — это верхняя граница (худший случай) по времени, которое необходимо для получения доступа к N байтов памяти (или, соответственно, N одинаковых по размеру элементов).

Так — да, почти сходится. Вместо O(N) из-за кэша получается что-то вроде O(√N), когда есть повторные обращения. Но не вместо O(1), не вместо единичного чтения! И замеряется не худший случай, а средний.


Аналогия с хождением по библиотеке странная. Когда контроллер памяти выставляет 32 бита адреса, он сразу (через фиксированное число тактов) получает доступ к любому из 2^32 байт информации. Если 64 бита — к любому из 2^64 байт. Никто никуда не ходит! Латентность не растёт пропорционально корню количества данных.
Аналогия с чёрной дырой ещё страннее. При современных (невысоких) плотностях и объёмах наверняка можно хранить информацию на узлах в трёхмерной структуре в количестве, пропорциональном объёму структуры, а не площади.


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

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

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

Да, но do-синтаксис менее многословен и семантически больше подходит.

Имхо, выигрывает. В funcparserlib нет лишних управляющих структур (for/yield), которые есть в коде из статьи. Это значит, что без этих структур можно обойтись, и следовательно они не нужны.
И что-то мне подсказывает, что по 2 yield'а в каждой функции — не Python-way (это не выглядит просто).

Комбинаторные парсеры (пример выше) выглядят гораздо "изящнее", чем for'ы вместо последовательностей термов. (Если эти for'ы убрать, то и получатся комбинаторные парсеры. А так — "код в стиле барокко").
Рекурсивный спуск выглядит проще (но объёмнее).
И ещё есть всякие генераторы парсеров, которые должны работать быстрее.


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


И почему Python 2 ?

Весьма странно видеть MPI и OpenMP как замену SIMD. Они не занимаются векторизацией.


OpenMP — это распараллеливание программы между потоками, и оно помогает задействовать совершенно другие возможности процессоров (многоядерность), чем SIMD (широкие АЛУ).
MPI (если речь идёт о Message Passing Interface) — это вообще про обмен сообщениями между процессами в кластере. Я бы не хотел видеть такое в стандарте языка общего назначения.


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

Если фабрика может вернуть nullptr, вызов метода без проверки на nullptr не является "адекватным".

Класслоадер по-хорошему сначала должен искать стандартные классы (которые уже есть у его родителя), а потом — свои.

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


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

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


Навскидку:
  • Описанная ситуация с нестабильной версией HttpComponents (включить нестабильную библиотеку в ОС, и тем самым запретить её обновления без костылей? серьёзно?)
  • Ограничение 64К методов в .dex (конечно, этого хватит всем).
  • Отсутствие generic-ов в go (сойдёт и так, пока вам не понадобятся самописные коллекции).
  • Отказ от использования исключений в языках, в которых они есть (в их библиотеках на С++ и по инерции на Java).
  • Нестандартное для Java соглашение об именовании полей (префикс m).

В принципе, этого достаточно, чтобы подходить с осторожностью к использованию их библиотек/технологий.

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

Если вы не смогли описать в интерфейсе эти параметры, это проблема конкретного случая. Опишите их в документации, если число ядер или время обработки — действительно часть интерфейса. Из невозможности полностью описать интерфейс средствами языка не следует вывод, что LSP неверен.

В пределе Вы не можете изменить свой код сразу после того как поднимите руки над клавиатурой.

Если вы лезете с клавиатурой прямо в продакшен, то да.
А на практике код можно менять как угодно, если вы в состоянии переписать все участки программы/инфраструктуры, на которые влияет изменение, так, чтобы клиенты не пострадали. OCP, если его применять, только минимизирует число изменений других частей.

В статье очень много максимализма, как уже говорилось выше. Эти принципы намного гибче, чем вам кажется. И SOLID действительно помогает поддерживать и развивать огромные системы.
Согласен. Автопилот должен руководствоваться чёткими правилами (законами). Никакого выбора, тем более случайного.

Information

Rating
Does not participate
Registered
Activity