Pull to refresh
0
0
Василевский Александр @kalaider

User

Send message
os.system('ffmpeg -i "../m3u8_downloader/mp3/temp.ts" "../m3u8_downloader/mp3/temp.mp3"')

Скачал такой сегмент, внутри mp3. Не лучше будет минимизировать реенкод, добавив -c copy к флагам ffmpeg? Неизвестно, сколько уже раз файл перекодировался на стороне самого vk (при загрузке, при нарезке в HLS). Конечно, если среди сегментов не только mp3 попадаются, нужно будет перекодировать, но это легко проверить тем же ffmpeg/ffprobe.

Этот "ужас" — просто псевдокод, чуть-чуть приближенный к математике по синтаксису. Демонстрация подхода. Про хардкод скорее относится к фрагментам кода конкретно из этой части статьи, а именно к аналитическим солверам. Вы сами демонстрируете фрагменты с switch (func.Name) { case "sinf" ... }, собственно, вот. Здесь есть над чем поработать.


У Mathematica прекрасная документация. Так что многие вещи можно вынести оттуда. Есть достаточно много открытых проектов и на ней. В т.ч. всякие пакеты символьных вычислений над тензорами — вот где нужен хороший pattern matching! Я в свое время тоже с этим игрался в математике (маленький самописный тензорный движок) и был почти доволен.

Не знаю, поможет ли автору мой комментарий, но поскольку сам был вдохновлен подобной идеей когда-то (до практики руки не дошли), накопились некоторые соображения по теме. Я имел неплохой опыт работы с Wolfram Language и большинство замечаний будут так или иначе связаны с ним. Также спешу заметить, что в репозиторий с кодом я так и не заглянул, потому если что-то уже именно так и реализовано, но в статье не отражено (или я не понял) — оно и к лучшему (надеюсь).


Во-первых. Pattern matching — прекрасная концепция функциональных языков. И она же по сути является краеугольным камнем любых символьных манипуляторов. (Может быть дело пойдет проще в F#?) В примере из предыдущей статьи с "уплощением" дерева для операции + (т.е. перевод x + (y + ...) в x + y + ...) автор вынужден был использовать слишком общие паттерны вроде any, const, Num(1) и т.д. Т.е. никак нельзя взять и сопоставить целое поддерево. Было бы куда проще, если бы можно было задавать правила не вида


if expr == plus(const1, any1) && any1 == plus(const2, any2)

а прямо-таки


if expr == plus(any1, plus(any2, any3))

Для сравнения, так оно в Mathematica и реализовано. Сопоставить одно выражение с другим очень и очень просто:


MatchQ[plus[1, plus[2, plus[3, 4]]], plus[x_, plus[y_, z_]]] == True

Несложно и заменить все вхождения данного паттерна в дереве:


plus[1, plus[2, plus[3, 4]]] //. plus[x___, plus[y__], z___] :> plus[x, y, z]
(* prints plus[1, 2, 3, 4] *)

Плюс, поскольку такие общие (x___ в частности) паттерны все-таки довольно накладны, математика из коробки предлагает т.н. атрибуты. Например, атрибут Flat, будучи присвоен функции plus, лишает нас необходимости самим ее уплощать.


Если бы можно было писать произвольные паттерны и доставать из них все упоминающиеся переменные (plus(a_, b_) => {a = ..., b = ...}), все бы существенно упростилось.


Во-вторых. Что касается упрощений, тут дело гораздо сложнее. Расширенный pattern matching, конечно, поможет, но шаблоны могут быть довольно сложные. Опять же, математика решает этот вопрос в Simplify и FullSimplify путем перебора всех возможных комбинаций подвыражений данного выражения: к каждой комбинации применяется та или иная упрощающая тактика, оценивается качество упрощения. Про качество тоже отдельный разговор. Например, можно минимизировать количество листов дерева выражения. Или длину максимальной ветви дерева. Играя с этим, можно делать интересные вещи.


В Simplify применяются некоторые эвристики, так что не все комбинации выражений действительно берутся в расчет. Вот пример вывода шагов упрощения выражения x^2 + 2x + a + 1 до a + (1 + x)^2 (вывод FullSimplify будет куда длиннее; к сожалению, я не знаю способа вывести это в формате "до-после", тут только "до"):


1 + a + 2 x + x^2
2 x
x^2
1 + 2 x + x^2
(1 + x)^2
1 + x
a + (1 + x)^2
...

Код получения такого вывода:


Simplify[x^2 + 2 x + a + 1, TransformationFunctions -> {Echo, Automatic}]

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


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


В-четвертых. Раньше уже упоминали, что можно компилировать выражения прямо в IL-код или в какие-нибудь нативные представления AST (Expressions). Математика тоже так может, плюс она может и в машинный код. Где-то на хабре была статья о компиляции математических функций в IL, но, боюсь, ту, о которой помню, не найду. Вот другая.


В-пятых. Вообще я был вдохновлен подходом интерактивных proof assistant'ов (Coq, The KeY Project, Z3 и т.д.). И прямо-таки горел желанием что-то свое написать. Но не срослось, поскольку я оценил объем работ неподъемным. Идея была в том, чтобы на каждом из этапов упрощения позволять пользователю выбирать ту тактику упрощения, которая бы ему была более приемлема. Из вычислителя-упростителя система тогда превращается в настоящую систему компьютерного доказательства. Мы выдвигаем гипотезу, что \forall x . sin(x)^2 + cos(x)^2 = 1, доказываем ее как-либо и оформляем в виде теоремы основн_триг_тождество. Далее уже можно на нее ссылаться, чтобы упростить sqrt(1 - sin^2(x)), заменив, например, 1. С группировкой тактик и теорем по категориям и с добавлением изрядной доли эвристик можно даже пользователя привлекать минимально к процессу доказательства. Проблем здесь много — чего только стоит контекст (например, теорема верна, если только x \in \Reals или x \in \Complex), потому я не буду сильно много здесь размышлять.


В-шестых. Хорошо бы на каком-либо этапе более детально осмотреться. Я имею в виду, например, подсмотреть у Mathematica и у готовых open source продуктов (у Axiom есть прямо-таки теоретические материалы, правда на поверку весьма скудные, где, кроме прочего, есть и аннотированные исходники). К сожалению, по вопросам систем компьютерной алгебры (СКА, CAS) крайне мало подробной информации (во всяком случае мне попадалось).

Да это ж Microsoft просто решил поправить баги в gentoo.
А вот поддержка на уровне языка (в виде расширения синтаксиса) может и была бы интересна.

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


Но это в целом. Что же касается оператора Элвиса, не считаю конкретно его необходимым на уровне языка, даже при том, что он весьма удобен. И хотя он и удобен, мы теряем контроль над потоком управления, получаем дополнительную сложность в отладке, дополнительный оверхед при чтении таких конструкций, особенно в случае длинных цепочек вида a?.b.c?.d?:e. Повторюсь, не стоит тащить все, что удобно, в язык, тем более такой консервативный язык, как java.

Кроме стримов он пока нигде не используется. Не использовался. До Java 9. С Java 9 он начал появляться в новых APIшках. Местами в старых. ServiceLoader, например, где он как раз и уместен — те самые "источники данных", о которых я упоминал ранее. Но в целом Optional сейчас есть только в стримах и в фишках вокруг них.

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

Не со всеми примерами данного цикла статей я согласен. А вот с Brian Goetz (судя по приведенному высказыванию в переводе автора) — напротив.


  • Optional не стоит злоупотреблять. Он точно не должен быть полем класса. Хотя бы из приведенных в статье соображений.
  • Он уж точно не должен возвращаться геттерами полей класса. Геттеры должны быть прозрачными, а не плодить лишние обертки.
  • Он не должен использоваться в прозрачных сеттерах полей.
  • Его не надо использовать там, где это ведет к загромождению интерфейса и к увеличению объема кода со стороны пользователя. Яркий пример — Map-ы, List-ы и им подобные. Ни в коем случае нельзя возвращать Optional на запрос значения по ключу/индексу, если пользователь явно не попросил вернуть именно Optional.

Что касается значений по умолчанию, то использовать или нет здесь Optional — весьма спорный вопрос. Классический вариант с null, 0 или -1 чаще оказывается нагладнее и правильнее. Да, Optional "безопаснее", т.е. он подстегивает программиста проверять, действительно ли значение есть, а также избавляет от магических чисел, но какой ценой… Ценой подсовывания вместо реального объекта обертки над ним! А это, знаете ли, протекание реализации в контракт метода, причем не самое хорошее. Возможность возврата или установки null должна быть оговорена в контракте метода, но именно оговорена (задокументирована, зааннотирована), а не обернута в Optional или еще что-то.


Что касается NullPointerException (NPE в народе), то это вполне штатная ситуация времени разработки. Я считаю, что проверка на null входных значений (сеттеров, методов, конструкторов) всегда должна присутствовать в явном или неявном виде. Для этого даже стандартных helper-метод есть. Бояться словить или бросить NPE не надо. Просто условия их возникновения или не возникновения, как ранее было сказано, должны присутствовать в контракте метода. И не в виде Optional, поскольку это сбивает с толку, заставляет оборачивать аргументы в Optional насильно, а также приводит к полной неразберихе, когда в старых кусках кода значения по умолчанию обозначается через null, а в новых — уже гордо через Optional. Кроме того, планирование интерфейсов усложняется. И тут может закрасться мысль вообще все оборачивать в Optional...


Вот в каких случаях использование Optional действительно целесообразно и оправдано:


  • Стримы.
  • Методы-генераторы или источники данных. Например, получение (первого) объекта из БД по запросу. Здесь Optional будет выглядеть элегантнее null или xxxOrDefault. Про коллекции см. выше.
  • (Сомнительно и с опаской) Конструкторы и методы изменения состояния (в отличие от прозрачных сеттеров). См. Socket. Однако переусердствовать тоже не стоит. В прозрачные сеттеры и прозрачные конструкторы лучше все-таки по старинке передавать null. А логику переключения состояния, если какой-то объект устанавливается в null/Optional.empty() — по старинке обрабатывать отдельным методом или прописать поведение в контракте (что не всегда приемлемо).

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

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

За что минусуем, товарищи? Мне тоже глаз режет. Хотя о таком все-таки лучше в личку.

Ну, возможно, Вы и правы. За счет сужения области применения получаем более оптимальную трансляцию. (Но, опять же, не компиляцию.)


Честно говоря, я всегда был убежден, что синтаксические конструкции — просто синтаксический сахар. Те же async/await в Boost прекрасно "эмулируются" переключением контекстов (Boost.Context)...


Надо будет как-нибудь попробовать Ваш инструмент в деле, чтобы не разглагольствовать попусту)

Да, хотелось бы взглянуть на результат работы. Вы его описываете-описываете) Надо же пощупать. Что же это за такие "еще некоторые интересные вещи"?

Сразу скажу, что я не являюсь сколь-нибудь квалифицированным специалистом по части "гетерогенных" вычислений (на кластере, на GPU и т.д.). Вот несколько мыслей, которые мне удалось из прочитенного и понятого сформулировать.


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


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


  3. Не понятно, как вообще компилятор / препроцессор будет организовывать работу в кластере.

По поводу оптимизаций компилятора сказать ничего не могу, но как-то интуитивно — особой разницы не будет. Поле для оптимизаций ведь у нас не очень большое — мне только стек и видится (первый пример в статье, например). А эмуляция стека в коде и "нативный" стек в принципе отличаются только тем, что при переполнении стековой памяти легко получить stack overflow. Поправьте, если несу бредятину)

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

С этим не спорю. Сюда же дополнительный профит в виде отсутствия затрат на отрисовку страниц.


Так что прощай Java, прощайте тесты на Python'е, прощай Selenium.

Однако автор, по моему скромному мнению, ощутимо более существенным выделяет тезис именно против Selenium и "скачков" из рабочего окружения (js) в тестировочное (java и т.д.), а главное событие — появление headless browser — как-то скралось на его фоне...


Странно, что вообще gui-free-режимы в браузерах стали появляться только сейчас. Автотесты вроде бы уже давно практикуются. И если разработчики тех же firefox и opera подключатся к тренду (в чем я немного сомневаюсь, во всяком случае в плане сроков ожидания данной фичи), то будет великое счастье уже всем тестировщикам и всем тем, кто так или иначе связан с написанием и развертыванием этих тестов.

Вот что мне не понятно, так это что мешало раньше использовать все то же самое с chrome в "обычном" режиме, если так не хотелось использовать Java+Selenium? Разъясните, пожалуйста.

Information

Rating
Does not participate
Location
Россия
Registered
Activity