Как стать автором
Обновить
29
3.2
Кирилл Белов @KirillBelovTest

Инженер по автоматизации тестирования

Отправить сообщение

Это не функция, а правило замены

addOne[x_] := x + 1

Не думаю, что вам нужно что-то доказывать, так как я помню, что вы хорошо знаете WL и читали предыдущие статье где я писал про правила и шаблоны. Но я должен поправить терминологию. Выше - это функция. Функция в WL - это символ, который имеет DownValues или SubValues (но вообще можно и с OwnValues создать "функцию", т.е. символ, который применяется к набору аргументов с квадратными скобками). Она только использует правило замены в определении, но сама по себе не является правилом замены. Правила замены это однозначно только:

rules = {
    x -> 10, 
    time :> Now
}; 

Функция определяется как

addOne = Function[{x}, x + 1]

А это принято называть "чистая функция". Если использовать вашу терминологию, то в итоге выражение Function[..] тоже сведется к правилу замены. Ведь абсолютно все определения символов в WL хранятся в списках определений в виде правил замены, примерно как в анекдоте про урок физики в церковной школе... 🤔

И по второму пункту. Как я и писал в статье - любой текстовый файл с кодом на WL можно назвать пакетом, но в статье я описал правила и рекомендации по разработке стандартизированных пакетов. Естественно, если вам не требуется пакет соответствующий "стандарту" вы можете просто создать один файл с расширением .wl, перечислить в нем определения, затем добавить папку с ним в $Path и использовать без всех тех ритуалов что я описал. Т.е. например файл MyPack.wl в директории ~/MyPack:

func1[x_, y_] := x + y; 

Код добавления пакета в пути поиска (один раз в $UserBaseDirectory/Kernel/init.m):

$Path = DeleteDublicates[Append[$Path, "~/MyPack"]]; 

И в блокноте:

<<MyPack`

func1[1, 2] (* => 3 *)

В итоге папка MyPack всего с одним файлов уже будет готовым пакетом, просто не по стандартам WRI.

И второе, что я хотел сказать по этому пункту. Дело в том, что между WL и python есть различие в структуре кода и файлов. Что касается импорта пакетов, то python обрабатывает файловую структуру, а сам код уже на втором месте. То есть создав простой модуль его имя и способ загрузки зависят от файла и его расположения, но не от кода внутри. А в WL в первую очередь важен сам загружаемый код и неважно где он находится. Создать и загрузить в сессию полноценный пакет можно прямо в самой сессии, для этого даже не нужно создавать никакие файлы. А вот PacletManager как раз стандартизирует подход к организации паклетов именно через структуру файлов и папок, то есть смещает фокус в сторону, которая ближе к Python.

Сначала отвечу на первый пункт.
Во-первых, как говорят Джейсон Стэтхэм и тимлид нашей команды: "Лучше меньше кода, чем больше кода. 400 строк - это хороший мердж-реквест, 20 строк - отличный, а если удалил 100 строк, то просто великолепный". Привычный глаз легко разбирает все псевдонимы и сокращения на WL, но их такое огромное разнообразие, что я, например, постепенно от них отказываюсь и оставляю только самые лаконичные и очевидные. Например, вот это на мой взгляд хорошо:

Map[func] @ {1, 2, 3, 4}

А вот это уже значительно хуже, так как тут явное злоупотребление для одной строки, хоть и совсем небольшое:

Exp[#^2 - Sin[#] + Log[x^1/2]& /@ {1, 2, ,3 , 4}

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

Во-вторых, это какой-то стереотип на счет итераторов и того что они являются плохим тоном в WL. Да, For очень неудобен, но While - это самый удобный способ создать бесконечный цикл, а Table самая лучшая, читаемая и очевидная функция для обработки списков. Даже не смотря на то, что я часто пользуюсь функциями высшего порядка - Table для меня не является чем-то плохим. К стандартным циклам вроде For такое отношение из-за их громоздкости и того, что они не локализуют итератор. И кстати говоря в Table вполне работает автокомпиляция как и в Map.

Спасибо! Кроме самой Mathematica я использую VS Code вместе с официальный плагином для Wolfram Language от Wolfram Research. Он бесплатный. Еще если его правильно настроить (указать путь до ядра), то он начинает работать значительно лучше - показывает usage и лучше работает автодополнение.

Кроме этого я упоминал про бесплатный интерфейс WLJS Notebook для Wolfram Engine который разрабатывает @JerryI и я.

А еще у меня есть старая статья с обзором бесплатных инструментов для WL. Сейчас часть информации оттуда устарела, может быть в будущем я напишу продолжение. Если у вас есть еще вопросы - с радостью отвечу =)

Да, это сработает, но противоречит правилам, которые предлагаются в WRI. Без usage примерно тоже самое, но ближе к конвенции это вот так (подразумеваются двойные переносы):

BeginPackage["ContextName`"];

SomeVariable;

SomeFunction;

Begin["`DontLookAtMe`"];

SomeVariable = 777;

SomeFunction[_] := RandomInteger[{0,10}];

End[]; 

EndPackage[];

В итоге в сессии будет доступно сразу:

func
var
someVariable
someFunction

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

@GorwinH скорее всего имел ввиду создание функции которая применяет другую функцию к списку, т.е. применение фукции высшего порядка

Ну если надо применить сложение к списку то в WL это вот так:

sum = Apply[Plus]

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

Часто причина появления open-cource кода не в том, что автор перечитал манифест Столлмана, а в том, что есть закрытый коммерческий продукт, который нужен разработчику, но ему оказывается проще сделать открытую альтернативу. Все способы монетизации, которые вы предложили просто противоречат этой цели. Если автор попытается open-source монетизировать "в лоб" (сам код, а не поддержку и не услуги), то этот код автоматически перестает быть открытым. Значит он и не нуждается в монетизации, иначе он вообще исчезнет из природы!

Вот как в WL с размерностями можно работать
https://imgur.com/a/Rt8uwWQ

Вы можете установить бесплатный fronted (который разрабатывает @jerryi и я). Там интерфейс удобнее чем командная строка. Когда у меня будет время я постараюсь сделать руководство для тех, кто впервые знакомится с языком. А пока что есть вот такая статья https://tproger.ru/articles/besplatnye-instrumenty-dlja-wolfram-language. Некоторые пункты там конечно уже не актуальны (например редактора Атом уже нет), а новых инструментов тогда еще не было

https://github.com/JerryI/wolfram-js-frontend/releases

Вы в FullSimplify два выражения через запятую написали. Поставьте вместо запятой “==“.

В общем-то я понял в чем проблема. Вы видимо установили wolfram engine, запустили ядро из командной строки и в нем в режиме REPL выполняли код. Но там можно по одной строчке выполнять только. Это же просто приложение командной строки как интерпретатор Python. Так вот вы когда два раза Cases ввели - то их результат просто умножился. Т.е. в WL умножение - это пробел как в матанализе. a b === a*b. Но! Для бесплатного ядра есть бесплатный фронтенд про который я и мой друг писал здесь же совсем недавно. @JerryI тоже может рассказать об этом

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

Добрый день! Вы использовали Wolfram Engine?

Спасибо большое за оценку моих трудов =)
Кстати мой любимый книжный пример - это сортировка пузырьком при помощи шаблонов:

{5, 4, 2, 6, 3, 2} //. 
   {fst___, x_Integer, y_Integer, rst___} :> 
     {fst, y, x, rst} /; x > y
  • //. - сокращение для ReplaceRepeated - функция которая выполняет повторяющуюся замену

  • :> - правило замены

  • /; - дополнительное условие для замены, но не обязательное

В итоге этот код на первом шаге делает следующее. Берет весь список и сравнивает его с образцом:

MatchQ[{5, 4, 2, 6, 3, 2}, {fst___, x_Integer, y_Integer, rst___}] /; x > y

связываются fst = <ничего>; x = 5; y = 4; rst = 2, 6, 3, 2. Оказывается что 5 > 4, тогда условие выполнено и на место исходного списка вставляется:

{fst = <ничего>, y = 4, x = 5, rst = 2, 6, 3, 2} == {4, 5, 3, 6, 3, 2}

Далее этот процесс повторяется, но теперь первый и второй элемент не проходят условие и происходит новое связывание: fst = 4; x = 5; y = 2; rst = 6, 3, 2 и т.д. пока не окажется так, что заменять ничего.

Да, конечно. Это такой синтаксис шаблонов/паттернов/образцов в WL. Во многих языках программирования, в которых есть паттерн-матчинг есть такая штука как wildcard. То есть "_". Так вот в WL шаблоны могут состоять из разных вариаций wildcard. Вот несколько примеров. Сравнение происходит при помощи функции MatchQ[expression, pattern]:

MatchQ[x, _] == True (*одиночное подчеркивание - все что угодно*)
MatchQ[f[x], f[_]] == True (*это значит выражение слева это f с любым аргументом*)
MatchQ[f[x, y], f[_]] == False
MatchQ[f[], f[_]] == False

MatchQ[f[1, 2, 3, 4], f[__]] == True (*два подчеркивания от одного до бесконечности элементов*)
MatchQ[f[], f[__]] == False

MatchQ[f[], f[___]] == True
MatchQ[f[x], f[___]] == True
MatchQ[f[x, y, z], f[___]] == True (*три подчеркивания любое число элементов от 0 до беконечности*)

Кроме трех вариантов wildcard есть еще возможность указывать диапазон количества элементов или конкретное значение, но это я оставлю на потом. И так мы рассмотрели возможность матчить произвольное выражение с шаблоном, который соответствует одному "чему угодно", одному и более или нулю и более штук "чего угодно". Но что если я хочу чтобы это было не что угодно, а только выражения/объекты с определенными типами? Тогда после подчеркивания я могу напрямую указать этот тип. Как понять какой тип у выражения в языке с динамической типизацией? При помощи функции Head. Это значит что

Head[1] == Integer
Head[1.0] == Real
Head[1/3] == Rational
Head[1 + I] == Complex
Head["string"] == String
Head[x] == Symbol
Head[f[x]] == f
Head[f[x][y][z]] == f[x][y]
Head[Head[f[x][y][z]]] == f[x]

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

MatchQ[1, _Integer] == True
MatchQ[1.5, _Integer] == False

То есть это wildcard с проверкой типов, но не все так просто.

Вот например как можно проверить что у нас массив только целых:

MatchQ[{1, 2, 3, 4}, {__Integer}] == True

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

MatchQ[{1, 2, 3, 1/3, 1/5, 1/7, I, I/3}, {__Integer, __Ratonal, ___Complex}] == True
MatchQ[{1, 2, 1/5}, {__Integer, __Ratonal, ___Complex}] == True

А еще wildcard можно связать с именем вот так:

MatchQ[f[1], f[x_]] == True

В MatchQ это бесполезно, но в функции, которая вытаскивает что-то по шаблону - это полезно:

Cases[{f[1]}, f[x_] :> x] (* => {1} *)

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

Cases[{f[1, 2, 4]}, f[x__] :> {x}] (* => {{1, 2, 4}}*)

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

f[x_] := x + 1

Я говорю математике о том, что как только она встречает выражение, которое матчится с:

MatchQ[f[1], f[x_]] == True

То сразу же происходит замена на правую часть определения функции - т.е. на x + 1. Определив так функция - я отсекаю все шаблоны, которые не матчатся с шаблоном f[x_]. Вызвав функцию на том, что не матчится ни с одним из шаблонов, которые были определены - ядро WL вернет результат ввода как есть. Т.е. например:

f[x_, y_] := x + y

f[1, 2, 3] (* => f[1, 2, 3] без изменений*)

Ну и возвращаясь к примеру из моего комментария выше шаблон

f[arr: {a1_Integer, ___Integer}, x_Integer] /; x < a1 := ...

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

  1. Первый аргумент список, где первый элемент списка обязательно целое число, а дальше может быть от нуля до бесконечности только целы чисел. Но я не стал писать a__Integer, чтобы связать ТОЛЬКО первый элемент списка с переменной a1, а не всю последовательность.

  2. Второй аргумент - просто целое число.

  3. После знака /; идет условие, которое может использовать связанные переменные

Если у вас возникнут вопросы после моего объяснения или я объяснил слишком запутанно - то напишите пожалуйста, я готов ответить на любые вопросы!

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

f[arr : {a1_Integer, ___Integer}, x_Integer, default_Integer : -1] /; x < a1 := default

f[arr : {___Integer, an_Integer}, x_Integer, default_Integer : -1] /; x >= an := an

f[arr : {__Integer}, x_Integer, default_Integer : -1] := 
With[{halfLen = Round[Length[arr]/2]}, 
  If[arr[[halfLen]] < x, 
    f[arr[[halfLen + 1 ;; ]], x, arr[[halfLen]]], 
    f[arr[[ ;; halfLen]], x, default]
  ]
]

f::argex = "Argument exception"; 

f[args___] := (Message[f::argex]; Defer[f[args]])

На скриншоте код и результаты тестов. Результаты не уместились, но тестов больше чем в самой статье, т.е. они те же самые, а еще включают то, что не было учтено.

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

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

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

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

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

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

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

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

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

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

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

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

Информация

В рейтинге
818-й
Откуда
Саратов, Саратовская обл., Россия
Зарегистрирован
Активность