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

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

Оперативно! Спасибо за статью.

Чем мог! ?
Когда смогу постараюсь высказать свои мысли про остальное

В одном из проектов у меня используется перегрузка деления, чтобы объединять объекты в коллекции без использования скобок, например:

box+=mp3/delay/waveout;

Но у Вольфрама последовательное деление автоматически преобразуется и получаем

\text{mp3}\text{+=}\frac{\text{mp3}}{\text{delay}\text{waveout}}

Как быть в этом случае?

Я думаю надо перегрузить Times и Power или Superscript.
вероятно Кирилл лучше знает...

то что придумал

myObj /: Times[myObj[t1_], Power[myObj[t2_], -1]] := collection[myObj[t1], myObj[t2]]
myObj["mp3"] / myObj["wave"]

но в принципе можно и без врапперов myObj

Ну если честно это какой-то антипаттерн. Синтаксис такой, какой он есть и конечно лучше бы ему следовать. У меня например была перегрузка точки, чтобы свойства объектов можно было получать и назначать вот так obj1.Name и obj1.Name = "Name", но отказался от этого чтобы не ломать лишнего.

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

box = MyBox[{}]

MyBox /: AddTo[box_MyBox, Times[a_, p : (Power[_, -1] ..)]] := 
 MyBox[Join[box[[1]], {a}, {p}[[All, 1]]]]

MyBox /: Set[name_Symbol, box_MyBox] := 
 (
  name /: AddTo[name, v_] := With[{b = name}, name = AddTo[b, v]]; 
  Block[{MyBox}, name = box]
  )

box += a/b/c

box += e/f/g/h

только надо чтобы In[151] выполнялся после In[153], я выполнил несколько раз - поэтому у меня сработало

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

на c# выглядит более дружелюбно к читателю вроде бы
        #region Collector
        public static ModuleCollector operator /(Module module1, Module module2)
        {
            ModuleCollector mc = new ModuleCollector();
            mc.Modules.Add(module1);
            if (module2 != module1)
                mc.Modules.Add(module2);
            return mc;
        }

        public static ModuleCollector operator /(ModuleCollector mc, Module module)
        {
            if (!mc.Modules.Contains(module))
                mc.Modules.Add(module);
            return mc;
        }

        public static ModuleCollector operator /(Module module, ModuleCollector mc)
        {
            if (!mc.Modules.Contains(module))
                mc.Modules.Add(module);
            return mc;
        }
        #endregion

Про антипаттерн не согласен. Всё-таки исторически символ деления - это двоеточие или горизонтальная черта. А прямой слэш используется для других целей не менее редко, в том числе и для перечисления.

Я все таки за то, чтобы была точность в формулировках. Когда вы говорите «хочу переопределять одно, а переопределяли другое» это неверно. Этого «одного» вообще нет в языке. В WL нет операторов и в том числе нет оператора деления. Это такой синтаксический Сахар для записи математических выражений. Если бы вы хотели переопределять то, что существует в том виде в каком записывается - это делает намного проще чем я показал и чем это сделано в c#. Например Plus или Times

Но ведь можно предложить и обратную ситуацию. Как в C# переопределять «//.» Или «/*» или “@@@“или нижний индекс?

В WL нет операторов и в том числе нет оператора деления

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

Просто скопировал из Вольфрама и вставил сюда, дробь на слеш он поменял самостоятельно, равно как и верхний индекс
F/:F[a_,x_]+F[b_,z_]:=F[a+b,x+z]
F/:F[a_,x_]-F[b_,z_]:=F[a-b,x-z]
F/:F[a_,b_]*F[c_,d_]:=F[ac+bd,bc+ad+bd]
F/:F[a_,b_]/F[c_,d_]:=F[(-bd+a(c+d))/(c^2+cd-d^2),(bc-ad)/(c^2+cd-d^2)]
F/:F[a_,b_]**F[c_,d_]:=F[ac-bd+ad,bc-ad]
F/:F[a_,b_]^n_:=Product[F[a,b],{k,1,n}]

Это Фибоначчионы, новое слово в математике)

Но ведь можно предложить и обратную ситуацию. Как в C# переопределять «//.» Или «/*» или “@@@“или нижний индекс?

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

Я правильно понимаю, что в C# есть оператор деления и для его переопределения вы написали класс с тремя методами. Более читабельно там только то, что в сигнатуре написано “operator /“. Плюс это конкретный класс, который может использовать только конкретные типы, а если делать обобщенный тип, то использовать его будет еще чуть-чуть сложнее. И последнее - вы не написали переопределение для “+=“, а ведь это еще один метод.

Для того же самого в WL я сделал две функциии, которые одновременно изменили поведение AddTo/Power/Times. В принципе это можно было бы и в одном определении сделать. Но я сделал так, чтобы при применении к выражению возвращалось новое выражение, а при применении к символу - изменялся сам символ. И все это с учетом того, что оператора или функции деления в WL нет.

Далее наоборот. Я предложил переопределить оператор «которого нет» в C#, вы сказали это невозможно.

Так в итоге в чью пользу складывается «зачет» по «гибкости синтаксиса»?

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

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

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

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

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

Прошу прощения, я не хотел, чтобы эта дискуссия приобрела негативную эмоциональную окраску. Переопределения += в моём примере не было, потому что он определён только для другого, более высокоуровнего типа, и по синтаксису ничем не отличается.

Задачу, которую мне не удалось решить в Вольфраме - это DSP-процессор. В более простых вариантах - спектральный анализатор и генератор сигналов в режиме реального времени. Возможно просто потому, что я не достаточно хороший программист. Но мне же удалось её решить и на c#, и на c++.

скриншот

Я вернулся. Честно сказать не знаю, что такое DSP-процессор, хоть и нашел расшифровку. НО! Я понимаю, что скомпилированный вариант всегда будет быстрее и лучше работать для обработки сигнала в реальном времени, но в теории на WL можно сделать такое же приложение. Я не дам прямо сейчас конкретный пример, но вы можете:

  • Использовать stream = AudioStream[] чтобы получить текущий поток с устройства ввода - например с микрофона

  • Получить его свойства:

    • Текущий кусок stream["CurrentAudio"]

    • Изменить размер буфера stream["BufferSize"] = n

    • Получить текущую позицию stream["Position"]

    • Если вы читаете из файла, то позицию в потоке можно изменять

    • Аудио поток можно записывать.

    • Потому записанный обработать.

  • Объект Audio ведет себя примерно как обычный массив точек, который представляет собой сигнал. Его можно легко изменить применив любую математическую функцию. Сигналы можно делить и объединять.

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

Спасибо, посмотрел ещё раз. Давайте зайдём с другой стороны. Итак, я выполнил код:

s = AudioStream[Function[t, Sin[1000 2 Pi t]]];
AudioPlay[s]

Вроде работает, но на слух явственно слышу искажения. Подключаю спектроанализатор и вижу следующее:

спектр

Гармоники на уровне 60 дБ! Это никуда не годится. Для сравнения, спектр синусоиды из моей программы:

спектр

Это скорее всего следствие неявной передискретизации, причём низкого качества. Для развлечения или игр подобное поведение сгодится, для профессиональной обработки звука - никак нет.

Далее. Ищем словосочетания "wolfram asio" и "wolfram wasapi" - их нет. Это значит, нельзя выбрать конкретный драйвер для вывода звука, нельзя выставить режим "Exclusive" и уж тем более нельзя подключить VST-плагин.

Далее. Выполняю

$AudioInputDevices

и в результате

{"System Setting", "Микрофон (Realtek High Definition Audio)"}

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

Далее. Даже если получится реализовать поточную передачу звука со входа на выход, то какие у Вольфрама есть готовые решения для его обработки? Правильно - никаких. Все функции из раздела "Audio Processing" ориентированы исключительно на массивы конечной длины, и компоновать их как узлы графа тоже не получится.

Пока что я все еще не до конца понял, что делает приложение на скриншоте

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

Вероятно лучший вариант для тех, кто хочет обрабатывать сигналы реального времени на WL будет LibraryLink + Asio, а потом уже обрабатывать их с помощью высокоуровневой математики как хочется…

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

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

Публикации

Истории