Комментарии 15
Оперативно! Спасибо за статью.
В одном из проектов у меня используется перегрузка деления, чтобы объединять объекты в коллекции без использования скобок, например:
box+=mp3/delay/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 не решал задачу из реального мира, а просто как демку для галочки сделали. Собственно это в духе Стивена
ООП в Wolfram Mathematica