Как стать автором
Обновить

Комментарии 64

даже по этому куску видно, что фортранчик-то нехило развивали с 1994, когда я на нем последний раз что-то написал :)

Все эти челленджи с многими языками - только и годятся что для видео и впечатляющих графиков.

Почеу никто не решает задачу "из жизни"? Например - для 10000 пользователей выбрать их любимые числа из базы. Ок, найдем их НОД, отправим письмом каждому по почте в pdf-вложении, а потом покажем на страничке. Все это - каждый день в 10:00.

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

Мне ещё не понятно, какой смысл смотреть на обёртки над библиотечной GCD?

Сравнить синтаксис языков.

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

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

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

Что мешает изменить реализацию стандартной библиотеки? Если вдруг в будущем появится более эффективный способ

Например - для 10000 пользователей выбрать их любимые числа из базы. Ок, найдем их НОД, отправим письмом каждому по почте в pdf-вложении, а потом покажем на страничке. Все это - каждый день в 10:00.

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

Для этого и придумали свои почтовые сервера ?

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

При желании можно сделать многое на многом. Работу в базой данных на Haskell, работу с pdf на Ruby и отправку емейла с Julia. При желании зачастую ничто не мешает скрестить одно с другим под видом третьего. Или например какой нибудь Cobol на котором работает некая часть экономической инфраструктуры в Америке. А возможно и не только там...

Вопрос удобства как исходной точки истины - не самый ведущий к результату. В начале нулевых или даже 80 не было такого обилия литературы и "удобства" среди языков как сейчас. Да и части языков в принципе не было. К тому моменту существующие языки были распространены в меру своих возможностей и на них писали. Несмотря на отсутствие удобств, на написанном руками ассемблере летали на луну. Несмотря на отсутствие удобств, есть ядра Linux и Windows NT/... которые все работают по всему миру на легаси коде написанных на не самых удобных языках при этом стабильно (разные сегментфолты или бсоды не берём в расчёт).

Языки которые предоставляют базовый функционал общего назначения - подойдут для почти всего. Делегировать на язык как на панацею который решает твои проблемы - ошибочно. Решать их должен программист. Но нынешний программист решать не хочет а хочет удобные ORM а за отсутствием таковых идёт писать статьи или делиться мнением почему язык ххх - в топку.

Вопрос бизнеса - это вопрос бизнеса. Вопрос удобства - вопрос личного навыка и личного желания.

НЛО прилетело и опубликовало эту надпись здесь

Так вот именно что куча вариантов. Так вот именно что есть огромное количество возможностей. Полностью согласен с вами. Было бы лишь желание. Чего к сожалению все меньше у программистов и с каждым годом все больше абстракций над слоном и нежеланием разбираться с другими технологиями где можно потратить условно на час больше но получить тоже самое и даже и лучше за счёт меньшего слонохеда. Или получить опыт например.

Боюсь спросить что будет лет через 20.

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

А всё почему: youtu.be/GVXJ0Y8IBb0?t=3025
НЛО прилетело и опубликовало эту надпись здесь

В BQN синтаксис шикарный:) Больше всего юникода.

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

Согласен. Особенно это заметно в таких языках, как Rust, где нужно много всего выразить. Ещё не хватает раскладки для программистов :) В QWERTY несколько напрягает зажимать шифт для нижнего подчёркивания.

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

скорость выполнения

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

А вот не факт. Если смотреть скорость выполнения и, главное, использование ресурсов процессора каким-нибудь инструментом тип PEX (Perfomace Explorer) который мы постоянно используем, то там отчетливо видно, что использование "удобных библиотек" в том же С++ ведет к ощутимому росту накладных расходов за счет постоянной работы с динамической памятью - выделение/освобождение и т.п. Что, например, черезмерное использование исключений (особенно пробрасывание их из одной области видимости в другую) ведет к снижению производительности по сравнению со старорежимным возвратом ошибки. Например, на нашей платформе есть понятие "структурированная ошибка" - содержит в себе код + набор параметров. Плюс специальные Message файлы в которых хранится текстовая расшифровка и уровень серьезности ошибки и откуда можно получить через системное API полный текст с подставленными параметрами. Это, например, позволяет хранить стек ошибок в процессе выполнения. И работает быстрее чем исключения (которые, впрочем, тоже есть).

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

Взять обмен данными между двумя модулями. Есть модный json, который так любят пихать везде. Очень универсально, поддержка в библиотеках для всех языков и т.п. Но вот задумайтесь - вы заранее знаете, что Модуль А может посылать модулю Б всего три типа пакетов, структура каждого из которых известна заранее. Ну так сделаейте датаграммный обмен - в заголовке укажите тип пакета и его длину, а на приеме просто посмотреть заголовок и смапить блок данных в один из трех возможных типов структур. Все. Не надо тратить время и ресурсы сначала на формирование json, потом на его парсинг. Да, не универсально. Но на то и голова дана чтобы каждый раз подумать - а нужна универсальность именно здесь и сейчас? Или можно обойтись без нее и получить выигрыш в эффективности?

К сожалению, об этом задумываются далеко не все.

Есть модный json, который так любят пихать везде. Очень универсально, поддержка в библиотеках для всех языков и т.п. Но вот задумайтесь - вы заранее знаете, что Модуль А может посылать модулю Б всего три типа пакетов, структура каждого из которых известна заранее. Ну так сделаейте датаграммный обмен - в заголовке укажите тип пакета и его длину, а на приеме просто посмотреть заголовок и смапить блок данных в один из трех возможных типов структур. Все. Не надо тратить время и ресурсы сначала на формирование json, потом на его парсинг.

А потом структура пакета изменится, поля местами поменяются, и "всё", счастливой отладки, держитесь там.

Для json есть более быстрые альтернативы - тот же messagepack, да и более удобочитаемые - типа yaml. Но json везде пихают потому, что он более распространён, библиотеку для его поддержки часто даже искать не надо.

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

Если для дебага надо - предусмотрите временное переключение на json. Или выводите куда-то в отдельный буфер и читайте там.

Если меняются поля или структура - ну тогда меняйте версию пакета или адрес отправки.

Сериализация структуры в json (строка) медленная, парсинг - тоже медленный. И валидация обязательно нужна по схеме же? Если мы берем тот же yaml - он медленней еще на порядок, хотя более читаемый, да.

У меня есть сервис куда раз в секунду надо слать MQTT-пакеты. Он поддерживает и json и protobuf. Protobuf - меньше в 3 раза и известного размера. А чтобы сериализовать float-число типа 3.42524574658785678 в json с не более чем 3 числами после запятой - без хаков - никак.

А чем messagepack не бинарный?

Согласен, он бинарный. Однако, проблемы остальные те же самые. Даже на его сайте пример в картинке - пакет в 18 байт для того чтобы передать 3 байта информации.

Да если кому-то хочется в перфоманс и не хочется в копирование при сериализации, тогда сразу брать какие-нибудь flatbuffers/capn proto.

Если поменялась структура - это уже другой пакет и другой обработчик. Зачем менять местами поля не могу придумать.

Датаграммный обмен вообще не требует библиотеки поддержки. И реализуется везде (малыми при этом затратами) - хоть на STM32 хоть где.

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

Все это требует минимума ресурсов и обеспечивает максимальную скорость.

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

Вы рассматриваете случай, когда всё идеально работает. А на практике - через 10 лет поменяли компилятор, выравнивание съехало, тесты прошли успешно, БД у заказчика повреждена (Ъ стори). Надёжнее использовать более структурированные данные. Потери ресурсов на msgpack не очень велики, часто они приемлемы. Чистые данные в датаграммах - это скорее годится для жёсткой оптимизации или для коротких проектов без поддержки.

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

И интернет и промавтоматизация всякая работает сильно больше 10-ти лет. А там подобные вещи (на уровне транспортных низкоуровневых протоколов) используются сплошь и рядом. Равно как и в различного рода очередях. Самый мой "долгоиграющий" проект начинался в 92-93-м годах. И пройдя через много стадий развития работает до сих пор. И в основе транспорта там как раз датаграммный обмен, причем, с достаточно большим количеством типов датаграмм, их маршрутизацией (контроллер нижнего уровня - контроллер верхнего уровня - микроядро - один или несколько интерфейсных клиентов и обратно). При то, что там еще при добавлении принципиально нового типа устройства, являющегося источником/получателем новых типов датаграмм, нет возможности останавливать или перезагружать систему - все делается "на горячую" - регистрируем в системе новый тип устройства, а потом уже идем на объект и физически его подключаем и оно сразу начинает работать.

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

А вот когда речь идет о действительно долгоиграющем проекте, да еще подразумевающим масштабирование, то, если сразу не думать за эффективность, то рано-поздно к вам с этим вопросом придут, уж поверьте опыту - то, что 10 лет назад нормально работало для 15млн клиентов, сейчас начинает тормозить когда клиентов стало 45млн.

Или когда все работает при 1000 устройств в системе, а на 5000 начинаются вылеты по таймаутам и перепосылки потому что принимающая сторона просто не успевает разбирать входящие пакеты и вовремя квитировать корректное их получение.

Речь шла про код в рамках задачи описанной в статье, а не про общий концепт замеров. То бишь подноготная всех этих алгоритмов использует старый добрый Евклидов алгоритм и при компиляции до некоторого -Os/-O3 уровня куски ассемблера будут крайне похожи. Поэтому разница между такими имплементациями будет на грани статистической ошибки. А то что не компилируется имеет некоторых оверхэд на поднятие VM/GC и для разных языков будет иметь разные значения. Поэтому и сказал что гонять бенчмарки для такого не имеет особого смысла. Если конечно автор не решит сделать Production Grade версии алгоритма с джейсонами и CI/CD.

Хаскель как всегда вне конкуренции. Пришлось взять листочек чтобы понять почему оно вообще работает. Придумавшему это человеку я бы с удовольствием поставил пиво.

Пришлось взять листочек чтобы понять

Я думал вне конкуренции - простота и максимальная ясность.

Тогда нужны паскаль и бейсик. Но их не включили

НЛО прилетело и опубликовало эту надпись здесь

Справедливости ради: реализацию fold все равно надо писать для каждого контейнера. Это сопоставимо с реализацией итератора для контейнера например в Питоне.

НЛО прилетело и опубликовало эту надпись здесь

Если под типом функции вы имеете type hints, то, видимо, как-то так:

from collections.abc import Iterable
from typing import TypeVar

T = TypeVar('T', int, float, complex)

def func(arg: Iterable[T]) -> T:
    pass
НЛО прилетело и опубликовало эту надпись здесь

На питоне типизация динамическая, и смысла в "Iterable[T]" немного. Тем более, что min и max принимают что-то вроде Iterable[T] where T:Comparable, а gcd принимает только Integer (а выясняется это в рантайме).

Так что:

def findGCD(nums: Iterable[int]) -> int:

А лучше даже без этого всего:

def findGCD(nums):

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

НЛО прилетело и опубликовало эту надпись здесь

Почему? Чем какой-нибудь uint32 из numpy хуже?

Действительно, uint32 принимает, а float'ы - нет.

Вероятно, целые числа, принимаемые gcd(), соблюдают некий контракт, проверяемый в рантайме, описание которого ещё поискать надо.

важно для простоты и максимальной ясности

Следующий заголовок на Rust нужен лишь для того, чтобы передать v в замыкание, передаваемое в библиотеку warp:

fn with_value<T>(v: T) -> impl Filter<Extract=(T, ), Error=Infallible> + Clone where T: Clone + Send {

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

Но дело в том, что аналогичный код на питоне без этих сотен символов тоже работает.

Или не работает, потому что в функцию передан не тот callable объект. Но вы об этом узнаете только в райнтайме.


И да, в коде на Rust ещё и ограничения на потокобезопасность и (глубокое) копирование — вещи, которые в Python просто не имеют смысла.

Или не работает, потому что в функцию передан не тот callable объект

После отладки - работает. Вопрос в том, что проще - отладить очевидный (в данном случае) код или написать на ровном месте что-то неочевидное.

Конечно, контрпримеры тоже есть, минусы динамической типизации я понимаю и предпочитаю всё-таки статическую.

в коде на Rust ещё и ограничения на потокобезопасность и (глубокое) копирование

Немного не угадали - фактически как Т используется arc<mutex<T2>>, поэтому глубокого копирования нет, несмотря на Clone.

НЛО прилетело и опубликовало эту надпись здесь

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

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

и парадокс в том, что код все равно работает (пусть и после отладки, <...>)

Ну то есть не работает.

Ну да, это все-таки python, его возможность по типизации очень ограничены.
В целом, можно создать свой Protocol:

import math
from typing import TypeVar, Iterable, Protocol, Any, runtime_checkable

@runtime_checkable
class Integral(Protocol):
    def __lt__(self, other: Any) -> bool: ...
    def __index__(self) -> int: ...

T = TypeVar('T', bound=Integral)

def findGCD(nums: Iterable[T]) -> T:
    return math.gcd(min(nums), max(nums))

Но это будет работать только с пользовательскими классами. Кроме того это все-равно только type hints, так что проверять тип все равно вручную придется.

class CustomInt:
    val: int

    def __init__(self, val: int):
        self.val = val

    def __lt__(self, other: Any) -> bool:
        if isinstance(other, CustomInt):
            return self.val < other.val
        raise TypeError("other is not CustomInt")

    def __index__(self) -> int:
        return self.val

    def __str__(self) -> str:
        return str(self.val)


if __name__ == '__main__':
    assert isinstance(1, Integral)
    assert isinstance(CustomInt(2), Integral)
    assert not isinstance(2.4, Integral)
    assert not isinstance("a", Integral)

    print(findGCD([CustomInt(6), CustomInt(5), CustomInt(7), CustomInt(8), CustomInt(4)])) 
 # prints 4

Короче, в какой момент это всё сломается, если попытаться написать что-то в духе

Сломается в рантайме в функции math.gcd(). Вряд-ли с этим можно что-то сделать, кроме как проходится по всем элементам и проверять тип явно.

На LabVIEW будет как-то вот так, тут это тоже библиотечная функция:

Без ассемблера никуда не годится!
вариант на Fasm
format PE GUI 4.0
entry main


section '.data' data readable

aNums       dd 183, 892, 445, 281, 863, 530, 602, 235, 356, 898, 639, 291, 582, 526, 941, 972
aNumsCount  = ($-aNums)/4


section '.code' code readable executable

main:
    mov     ecx, (aNumsCount-1)*4
    mov     eax, [aNums+ecx]
    mov     ebx, eax
maxmin:         
    sub     ecx, 4
    cmp     [aNums+ecx], eax 
    jle     notgreater     
    mov     eax, [aNums+ecx]
notgreater:          
    cmp     [aNums+ecx], ebx 
    jge     notless     
    mov     ebx, [aNums+ecx]    
notless:    
    test    ecx, ecx
    jne     maxmin
gcd:
    mov     ecx, eax
    test    ebx, ebx
    jne     cyrcle
    jmp     exit     
cyrcle:    
    xor     edx, edx
    div     ebx    
    test    edx, edx    
    je      fine
    mov     eax, ebx; ecx
    mov     ebx, edx
    jmp     cyrcle
fine:
    mov     eax, ebx     
exit:
    int3 ; result in eax, 3

Было бы любопытно попутно производительность их всех сравнить.

А где Brainfuck?
А вообще видео больше напоминает обзор одной из многих тысяч веток codegolf. Там даже поинтереснее будет.

Как правильно уже отметили - какой смысл сравнивать синтаксис вызова библиотечных функций?

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

Точно надо на детской задаче демонстрировать. Например:
Есть два прямоугольника один со сторонами a,b и второй со сторонами c,d
Написать функцию проверки: можно ли разместить второй внутри первого.

Можно задачку сделать чуть интересней ;)
Вершины заданы координатами.
Размещается ли второй прямоугольник четырехугольник внутри первого?

Тогда рекомендую проверять N-гранники с N порядка миллиарда в многомерном пространстве, чтоб уж какие-нибудь R-деревья навернуть, а не сводить весь алгоритм к логическому выражению.

Пересечение полигонов не так интересно и постановка задачи сложнее. Тут тоже можно прямоугольники вращать произвольным образом.
НЛО прилетело и опубликовало эту надпись здесь
Делал нечто подобное: github.com/kt97679/tetris Языков меньше — всего 7, но зато задача более сложная — тетрис.

Ожидал реализацию без встроенных функций, синтаксис лучше смотреть на learnxinyminutes.com там как-то нагляднее.

Решил посмотреть реализацию gcd на Rosetta Stone и нашёл там такой бриллиант на неком Golfscript:

;'2706 410'
~{.@\%.}do;

Output: 82 

А есть ли хит-парад у кого самый непонятный код? Мне кажется у Golfscrpt неплохие шансы попасть в ТОП.

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

Странно, что никто ещё не упомянул розетту - https://rosettacode.org/.

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

Их там больше тысячи и около 800 языков программирования, включая всеми любимый Brainfuck.

Что-бы понять рекурсию, нужно понять рекурсию.

На lua (как ни странно нет math.gcd())

#!/usr/bin/env lua

function gcd(a,b)
    if b == 0 then return math.abs(a)
              else return gcd(b,a%b)
    end
end

DataSet = {2, 9, 6, 10, 5}

print (gcd(
              (math.min
                  (table.unpack(DataSet))
              )
                  ,
              (math.max
                  (table.unpack(DataSet))
              )
          )
      )

На REXX, тоже самое. Разумеется там тоже нет gcd. Но дедушка старенький, дедушке простительно. ;-)

#!/usr/bin/env rexx

/* Find min, max and gcd of min and max */

DataSet="2, 9, 6, 10, 5"

INTERPRET "Min=min("Dataset")"
INTERPRET "Max=max("Dataset")"
SAY gcd(Min, Max)

EXIT

gcd: procedure
PARSE ARG a,b
IF b = 0 THEN RETURN abs(a)
RETURN gcd(b,a//b)

Да, бобик регистронезависимый язык и в нём нет зарезервированных слов. Так min() здесь и функция и Min переменная. Далее язык понимает по контексту. Но злоупотреблять этим конечно не стоит.

math.min(table.unpack(DataSet))

Распаковка таблицы на стек? Боюсь, это не всегда сработает.

Приходишь такой на первый рабочий день а там весь проект на APL

https://habrastorage.org/r/w1560/getpro/habr/upload_files/106/d14/a84/106d14a84d7abb7916c394b5a27b82af.png

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

вывод: питон гениален и его надо развивать

Зарегистрируйтесь на Хабре, чтобы оставить комментарий