Знай свои чистые аргументы функции
При игре в гольф вы часто используете функциональный подход, когда вы используете анонимные (чистые) функции с &
сокращенным синтаксисом. Существует множество различных способов доступа к аргументам такой функции, и вы часто можете сбрить пару байтов, хорошо разбираясь в возможностях.
Доступ к отдельным аргументам
Вы, наверное, знаете это, если раньше использовали чистые функции. П - й аргумент называется #n
, и #
действует в качестве псевдонима для #1
. Поэтому, если, скажем, вы хотите написать функцию, которая принимает в качестве параметров другую функцию и ее аргумент (чтобы передать аргумент этой функции), используйте
#@#2&
Это не работает с отрицательными числами (например, вы можете использовать при доступе к спискам).
Доступ к именованным аргументам (новое в V10)
Одна из основных новых возможностей языка в Mathematica 10 - это Association
s, которые в основном представляют собой карты значений ключей с произвольными типами ключей, написанными как
<| x -> 1, "abc" -> 2, 5 -> 3 |>
Если такая связь передается в качестве первого аргумента чистой функции, вы можете получить доступ к некоторым ее аргументам в виде именованных параметров:
{#, #2, #3, #abc, #xyz} & [<| "abc" -> "1st", "xyz" -> "2nd", abc -> "3rd" |>, "4th", "5th"]
(* {<| "abc" -> "1st", "xyz" -> "2nd", abc -> "3rd" |>, "4th", "5th", "1st", "2nd"} *)
Обратите внимание, что #
все еще относится ко всей ассоциации, как ожидалось. Чтобы именованные параметры работали, ключи должны быть строками (например, они не будут работать, если вы используете неопределенные переменные), и эти строки должны начинаться с буквы и содержать только буквы и цифры.
«Я» аргумент #0
Менее известной особенностью является то, что она #0
также существует и предоставляет вам сам объект функции. Это может быть очень полезно в квази и обобщенных квази. На самом деле, самая короткая Mathematica Quine (я знаю)
ToString[#0][] & []
Немного раздражает то, что он не даст вам точных символов, которые вы ввели. Например, если использовать @
для приложения функции, он все равно будет отображаться как [...]
и пробелы будут вставлены в некоторых местах. Обычно это приводит к тому, что квайн будет немного длиннее, чем хотелось бы, но он всегда будет работать, сначала играя в гольф, а затем просто копируя его вывод - который теперь должен быть настоящим квинем.
Помимо кавычек это также означает, что вы можете писать рекурсивный код без необходимости называть вашу функцию. Сравните эти три (наивные, но играющие в гольф) реализации Фибоначчи:
f@0=0;f@1=1;f@n_:=f[n-1]+f[n-2]
f@n_:=If[n<2,n,f[n-1]+f[n-2]]
If[#<2,#,#0[#-1]+#0[#-2]]&
Последовательности аргументов
Теперь здесь начинается настоящее волшебство. Последовательности не часто используются в гольфе, потому что Sequence
это слишком длинное имя, чтобы стоить его большую часть времени. Но в чистых функциях это то, где они сияют. Если вы не знакомы с последовательностями, они в основном похожи на знаки в некоторых других языках, если вы используете последовательность в List
списке аргументов или в функции, ее элементы будут автоматически расширены в отдельные слоты. Так
{1, Sequence[2, 3, 4], 5} == {1, 2, 3, 4, 5}
f["a", Sequence[0, {}], "b"] == f["a", 0, {}, "b"]
Теперь в чистых функциях ##
или ##1
есть последовательность всех аргументов. Аналогично, ##2
это последовательность всех аргументов, начиная со второго, ##3
всех аргументов, начинающихся с третьего и т. Д. Поэтому для начала мы можем просто переопределить Sequence
как ##&
, сохранив 5 байтов. В качестве примера использования это дает нам альтернативу Join@@list
(см. Этот совет ), которая не сохраняет байты, но о которой полезно знать в любом случае:
##&@@@list
Это эффективно выравнивает первый уровень вложенного списка. Что еще мы можем сделать с этим? Вот более короткая альтернатива на 2 байта RotateLeft
:
RotateLeft@list
{##2,#}&@list
Только для этих вещей стоит помнить эту особенность. Тем не менее, мы можем сделать лучше! Последовательности становятся действительно интересными, если учесть, что операторы фактически реализованы как функции под капотом. Например, на a+b
самом деле оценивает Plus[a,b]
. Так что, если мы дадим эту последовательность ...
1+##&[1,2,3]
=> Plus[1,##]
=> Plus[1,1,2,3]
=> 7
Этот прием использовался в этом совете, чтобы сохранить байт Times
, потому что технически сопоставление также просто оператор:
1##&[1,2,3]
=> Times[1,##]
=> Times[1,1,2,3]
=> 6
Вы также можете использовать его для сохранения байта, Unequal
если у вас есть односимвольное значение или переменная, которая, как вы знаете, отсутствует в ваших аргументах ( N
вероятно, будет работать в 99% случаев):
Unequal[a,b,c]
N!=##&[a,b,c]
Это становится еще более интересным с унарные и -
и /
- последние два фактически реализованы в терминах умножения и возведения в степень. Вот список вещей, которые вы можете сделать, где последний столбец предполагает, что функции переданы аргументы a, b, c
:
Operator Function Expanded Equivalent to
+## Plus[##] Plus[a,b,c] a+b+c
1## Times[1,##] Times[1,a,b,c] a*b*c
-## Times[-1,##] Times[-1,a,b,c] -a*b*c
x+## Plus[x,##] Plus[x,a,b,c] x+a+b+c
x-## Plus[x,Times[-1,##]] Plus[x,Times[-1,a,b,c]] x-a*b*c
x## Times[x,##] Times[x,a,b,c] x*a*b*c
x/## Times[x,Power[##,-1]] Times[x,Power[a,b,c,-1]] x*a^b^c^-1
##/x Times[##,Power[x,-1]] Times[a,b,c,Power[x,-1]] a*b*c/x
x^## Power[x,##] Power[x,a,b,c] x^a^b^c
##^x Power[##,x] Power[a,b,c,#] a^b^c^x
x.## Dot[x,##] Dot[x,a,b,c] x.a.b.c
Другие общие операторы !=
, ==
, &&
, ||
. Менее распространенные из них , чтобы иметь в виду , являются |
, @*
, /*
. В заключение приведу небольшой бонусный трюк:
#### Times[##,##] Times[a,b,c,a,b,c] (a*b*c)^2
Продолжайте экспериментировать с ними, и дайте мне знать, если вы найдете какие-либо другие полезные или особенно интересные приложения!
(Norm[#-#2]&)
вместоEuclideanDistance
.