Мне очень нравится, как Wordpress работает с его функциями enqueue и localize , поэтому после этой модели я написал простой класс для размещения скриптов на странице в соответствии с зависимостей сценария и получения дополнительных данных для скрипта.
class mHeader {
private $scripts = array();
/**
* @param string $id unique script identifier
* @param string $src script src attribute
* @param array $deps an array of dependencies ( script identifiers ).
* @param array $data an array, data that will be json_encoded and available to the script.
*/
function enqueue_script( $id, $src, $deps = array(), $data = array() ) {
$this->scripts[$id] = array( 'src' => $src, 'deps' => $deps, 'data' => $data );
}
private function dependencies( $script ) {
if ( $script['deps'] ) {
return array_map( array( $this, 'dependencies' ), array_intersect_key( $this->scripts, array_flip( $script['deps'] ) ) );
}
}
private function _unset( $key, &$deps, &$out ) {
$out[$key] = $this->scripts[$key];
unset( $deps[$key] );
}
private function flattern( &$deps, &$out = array() ) {
foreach( $deps as $key => $value ) {
empty($value) ? $this->_unset( $key, $deps, $out ) : $this->flattern( $deps[$key], $out );
}
}
function print_scripts() {
if ( !$this->scripts ) return;
$deps = array_map( array( $this, 'dependencies' ), $this->scripts );
while ( $deps )
$this->flattern( $deps, $js );
foreach( $js as $key => $script ) {
$script['data'] && printf( "" . PHP_EOL, key( $script['data'] ), json_encode( current( $script['data'] ) ) );
echo "" . PHP_EOL;
}
}
}
Функция enqueue_script()
предназначена для добавления сценария, установки сума и зависимостей в других сценариях и дополнительных данных, необходимых для скрипт.
$header = new mHeader();
$header->enqueue_script( 'jquery-ui', '//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js', array( 'jquery' ) );
$header->enqueue_script( 'jquery', '//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js' );
$header->enqueue_script( 'custom-script', '//custom-script.min.js', array( 'jquery-ui' ), array( 'mydata' => array( 'value' => 20 ) ) );
$header->print_scripts();
И, метод print_scripts()
вышеприведенного примера отправит этот вывод:
Независимо от того, что скрипт jquery находится в очереди после 'jquery-ui', он печатается ранее, потому что он определен в 'jquery-ui', который зависит от `jquery '. Дополнительные данные для «custom-script» находятся внутри нового блока сценариев и размещаются перед ним, он содержит mydata
объект, который содержит дополнительные данные, теперь доступный для «пользовательского сценария».
Я попробовал раз писать об этом, но в конце концов я сдался, так как правила несколько размыты. В принципе, вам придется повесить его.
Возможно, лучше сосредоточиться на том, где фигурные скобки и скобки могут использоваться взаимозаменяемо: при передаче параметров вызовам метода. Вы можете заменить скобку фигурными скобками, если и только если метод ожидает единственный параметр. Например:
List(1, 2, 3).reduceLeft{_ + _} // valid, single Function2[Int,Int] parameter
List{1, 2, 3}.reduceLeft(_ + _) // invalid, A* vararg parameter
Тем не менее, вам нужно больше знать, чтобы лучше понять эти правила.
Авторы Spray рекомендуйте круглые парсеры, потому что они дают повышенную проверку компиляции. Это особенно важно для DSL, таких как Spray. Используя parens, вы сообщаете компилятору, что ему должна быть предоставлена только одна строка; поэтому, если вы случайно дадите ему два или более, он будет жаловаться. Теперь это не так с фигурными фигурными скобками - если, например, вы где-то забыли оператор, тогда ваш код будет компилироваться, и вы получите неожиданные результаты и потенциально очень сложную ошибку. Ниже изобретено (так как выражения чисты и, по крайней мере, дают предупреждение), но делает точку:
method {
1 +
2
3
}
method(
1 +
2
3
)
Первые компиляции, вторая дает error: ')' expected but integer literal found
. Автор хотел написать 1 + 2 + 3
.
Можно утверждать, что это похоже на многопараметрические методы с аргументами по умолчанию; невозможно случайно забыть запятую, чтобы отделять параметры при использовании парнеров.
Важное часто забытое примечание о многословии. Использование фигурных скобок неизбежно приводит к подробному коду, поскольку руководство стиля Scala четко заявляет, что закрытие фигурных скобок должно быть в их собственной строке:
... закрывающая фигурная скобка находится на ее собственной строки сразу после последней строки функции.
blockquote>Многие автореформаторы, как и в IntelliJ, автоматически выполнит эту переформатировку для вас. Так что старайтесь использовать круглые паренны, когда сможете.
Инфиксная нотация
При использовании инфиксной нотации, например
List(1,2,3) indexOf (2)
, вы можете опустить скобки, если есть только один параметр и записать его какList(1, 2, 3) indexOf 2
. Это не относится к точечной нотации.Обратите также внимание, что если у вас есть один параметр, который является выражением с несколькими токенами, например
x + 2
илиa => a % 2 == 0
, вы должны использовать скобки для указания границы выражения.Кортежи
Поскольку вы иногда можете опускать скобки, иногда кортеж нуждается в дополнительных скобках, например, в
((1, 2))
, а иногда внешняя скобка может быть опущена, например в(1, 2)
. Это может вызвать путаницу.Функциональные / частичные литералы функции с
case
Scala имеет синтаксис для литералов функций и частичных функций. Это выглядит так:
{ case pattern if guard => statements case pattern => statements }
Единственными другими местами, где вы можете использовать
case
заявления, являются ключевые словаmatch
иcatch
:object match { case pattern if guard => statements case pattern => statements }
try { block } catch { case pattern if guard => statements case pattern => statements } finally { block }
Вы не можете использовать выражения
case
в любом другом контексте . Итак, если вы хотите использоватьcase
, вам нужны фигурные скобки . В случае, если вам интересно, что делает различие между функцией и литералом частичной функции, ответ таков: контекст. Если Scala ожидает функцию, вы получите функцию. Если он ожидает частичную функцию, вы получаете частичную функцию. Если оба они ожидаются, это дает ошибку в двусмысленности.Выражения и блоки
Скобки могут использоваться для создания подвыражений. Кудрявые фигурные скобки можно использовать для создания блоков кода (это не литер функций, поэтому не стоит пытаться использовать его как один). Блок кода состоит из нескольких операторов, каждый из которых может быть оператором импорта, объявлением или выражением. Это происходит следующим образом:
{ import stuff._ statement ; // ; optional at the end of the line statement ; statement // not optional here var x = 0 // declaration while (x < 10) { x += 1 } // stuff (x % 5) + 1 // expression } ( expression )
Итак, если вам нужны декларации, несколько операторов,
import
или что-то в этом роде, вам нужны фигурные скобки. И поскольку выражение является выражением, скобки могут появляться внутри фигурных скобок. Но интересно то, что блоками кода являются выражения также , поэтому вы можете использовать их где угодно внутри выражение:( { var x = 0; while (x < 10) { x += 1}; x } % 5) + 1
Итак, поскольку выражения - это выражения, а блоки кодов - выражения, все ниже:
1 // literal (1) // expression {1} // block of code ({1}) // expression with a block of code {(1)} // block of code with an expression ({(1)}) // you get the drift...
Где они не взаимозаменяемы
В принципе, вы не можете заменить
{}
на()
или наоборот в другом месте. Например:while (x < 10) { x += 1 }
Это не вызов метода, поэтому вы не можете записать его каким-либо другим способом. Ну, вы можете поместить фигурные скобки внутри в скобки для
condition
, а также использовать скобки внутри фигурные скобки для блока кода:while ({x < 10}) { (x += 1) }
Итак, я надеюсь, что это поможет.
Поскольку вы используете case
, вы определяете частичную функцию, а частичные функции требуют фигурных скобок.
Я не думаю, что в Scala есть что-то особенное или сложное в фигурных скобках. Чтобы справиться с кажущимся сложным использованием их в Scala, просто сохраните пару простых вещей:
Давайте объясним несколько примеров в трех предыдущих правила:
val tupleList = List[(String, String)]()
// doesn't compile, violates case clause requirement
val filtered = tupleList.takeWhile( case (s1, s2) => s1 == s2 )
// block of code as a partial function and parentheses omission,
// i.e. tupleList.takeWhile({ case (s1, s2) => s1 == s2 })
val filtered = tupleList.takeWhile{ case (s1, s2) => s1 == s2 }
// curly braces omission, i.e. List(1, 2, 3).reduceLeft({_+_})
List(1, 2, 3).reduceLeft(_+_)
// parentheses omission, i.e. List(1, 2, 3).reduceLeft({_+_})
List(1, 2, 3).reduceLeft{_+_}
// not both though it compiles, because meaning totally changes due to precedence
List(1, 2, 3).reduceLeft _+_ // res1: String => String = <function1>
// curly braces omission, i.e. List(1, 2, 3).foldLeft(0)({_ + _})
List(1, 2, 3).foldLeft(0)(_ + _)
// parentheses omission, i.e. List(1, 2, 3).foldLeft(0)({_ + _})
List(1, 2, 3).foldLeft(0){_ + _}
// block of code and parentheses omission
List(1, 2, 3).foldLeft {0} {_ + _}
// not both though it compiles, because meaning totally changes due to precedence
List(1, 2, 3).foldLeft(0) _ + _
// error: ';' expected but integer literal found.
List(1, 2, 3).foldLeft 0 (_ + _)
def foo(f: Int => Unit) = { println("Entering foo"); f(4) }
// block of code that just evaluates to a value of a function, and parentheses omission
// i.e. foo({ println("Hey"); x => println(x) })
foo { println("Hey"); x => println(x) }
// parentheses omission, i.e. f({x})
def f(x: Int): Int = f {x}
// error: missing arguments for method f
def f(x: Int): Int = f x
{}
. Я обновил формулировку для точности. И для 4, это немного сложно из-за взаимодействия между ()
и {}
, поскольку def f(x: Int): Int = f {x}
работает, и именно поэтому у меня был пятый. :)
– lcn
24 September 2015 в 20:18
fun f(x) = f x
действителен в SML.
– aij
26 September 2015 в 14:34
f {x}
как f({x})
, для меня лучше, чем объяснение i>, поскольку взаимозаменяемость мышления ()
и {}
менее интуитивная. Кстати, интерпретация f({x})
несколько подкреплена спецификацией Scala (раздел 6.6): ArgumentExprs ::= ‘(’ [Exprs] ‘)’ | ‘(’ [Exprs ‘,’] PostfixExpr ‘:’ ‘_’ ‘*’ ’)’ | [nl] BlockExp
– lcn
27 September 2015 в 02:45
Я думаю, что стоит объяснить их использование в вызовах функций и почему происходят разные вещи. Поскольку кто-то уже сказал, что фигурные скобки определяют блок кода, который также является выражением, поэтому можно поставить выражение, в котором ожидается выражение, и оно будет оценено. Когда оценивается, его операторы выполняются, а значение последнего оператора - результат оценки целочисленного блока (несколько как в Ruby).
Имея это, мы можем делать такие вещи, как:
2 + { 3 } // res: Int = 5
val x = { 4 } // res: x: Int = 4
List({1},{2},{3}) // res: List[Int] = List(1,2,3)
Последний пример - это просто вызов функции с тремя параметрами, каждый из которых сначала оценивается.
Теперь, чтобы увидеть, как он работает с вызовами функций, давайте определим простую функцию, которая возьмет другую функцию в качестве параметра.
def foo(f: Int => Unit) = { println("Entering foo"); f(4) }
Чтобы вызвать ее, нам нужно передать функцию, которая принимает один param из типа Int, поэтому мы можем использовать функциональный литерал и передать его foo:
foo( x => println(x) )
Теперь, как сказано ранее, мы можем использовать блок кода вместо выражения, поэтому давайте использовать его
foo({ x => println(x) })
Что здесь происходит, так это то, что код внутри {} оценивается, а значение функции возвращается как значение оценки блока, это значение затем передается в foo. Это семантически то же самое, что и предыдущий вызов.
Но мы можем добавить что-то еще:
foo({ println("Hey"); x => println(x) })
Теперь наш блок кода содержит два оператора и потому, что он вычисляется до выполнения foo , происходит то, что сначала печатается «Эй», затем наша функция передается в foo, «Ввод foo» печатается и, наконец, печатается «4».
Это выглядит немного уродливым, хотя Scala позволяет мы должны пропустить скобку в этом случае, поэтому мы можем написать:
foo { println("Hey"); x => println(x) }
или
foo { x => println(x) }
Это выглядит намного приятнее и эквивалентно предыдущим. Здесь сначала вычисляется блок кода, и результат оценки (который является x => println (x)) передается как аргумент для foo.
foo({ x => println(x) })
. Может быть, я слишком застрял на своих путях ...
– dade
12 May 2016 в 01:04
У сообщества есть возможность стандартизировать использование фигурных скобок и круглых скобок, см. Руководство по стилю Scala (стр. 21): http://www.codecommit.com/scala-style-guide.pdf
Рекомендуемый синтаксис вызовов методов более высокого порядка состоит в том, чтобы всегда использовать фигурные скобки и пропускать точку:
val filtered = tupleList takeWhile { case (s1, s2) => s1 == s2 }
Для «нормальных» вызовов методам вы должны использовать точку и круглые скобки.
val result = myInstance.foo(5, "Hello")
+
, --
), а не обычные методы, такие как takeWhile
. Вся нотация инфикс заключается в том, чтобы разрешить DSL и пользовательские операторы, поэтому их следует использовать в этом контексте не все время.
– samthebest
29 December 2014 в 09:51
Повышенная проверка компиляции с помощью parens
Авторы Spray рекомендуют, чтобы круглые парсеры увеличивали проверку компиляции. Это особенно важно для DSL, таких как Spray. Используя parens, вы сообщаете компилятору, что ему должна быть предоставлена только одна строка, поэтому, если вы случайно дали ей два или более, она будет жаловаться. Теперь это не так с фигурными фигурными скобками, если, например, вы забудете оператора где-нибудь, где ваш код будет скомпилирован, вы получите неожиданные результаты и потенциально очень сложную ошибку. Ниже изобретено (так как выражения чисты и будут хотя бы давать предупреждение), но делает точку
method {
1 +
2
3
}
method(
1 +
2
3
)
Первые компиляции, вторая дает error: ')' expected but integer literal found.
, которую автор хотел написать 1 + 2 + 3
.
Можно утверждать, что это похоже на многопараметрические методы с аргументами по умолчанию; невозможно случайно забыть запятую, чтобы отделять параметры при использовании парсеров.
Многозначность
Важное часто забытое примечание о многословии. Использование фигурных скобок неизбежно приводит к подробному коду, поскольку в руководстве по стилю скалы четко указано, что закрытие фигурных скобок должно быть в их собственной строке: http://docs.scala-lang.org/style/declarations.html «... закрывающая скобка находится на собственной линии сразу после последней строки функции». Многие автореформаторы, как и в Intellij, автоматически выполнит эту переформатировку для вас. Поэтому старайтесь использовать круглые парнеры, когда сможете. Например. List(1, 2, 3).reduceLeft{_ + _}
становится:
List(1, 2, 3).reduceLeft {
_ + _
}
С фигурными скобками у вас есть точка с запятой, вызванная для вас, а в скобках нет. Рассмотрим функцию takeWhile
, так как она ожидает частичную функцию, только {case xxx => ??? }
является корректным определением вместо круглых скобок вокруг выражения case.
Существует несколько различных правил и выводов: во-первых, Scala выводит фигурные скобки, когда параметр является функцией, например. в list.map(_ * 2)
выводятся фигурные скобки, это всего лишь более короткая форма list.map({_ * 2})
. Во-вторых, Scala позволяет пропустить скобки в последнем списке параметров, если в списке параметров есть один параметр, и это функция, поэтому list.foldLeft(0)(_ + _)
можно записать как list.foldLeft(0) { _ + _ }
(или list.foldLeft(0)({_ + _})
, если вы хотите быть дополнительным явным).
Однако, если вы добавите case
, вы получите, как указывали другие, частичную функцию вместо функции, а Scala не будет выводить фигурные скобки для частичных функций, поэтому list.map(case x => x * 2)
выиграл 't работают, но оба list.map({case x => 2 * 2})
и list.map { case x => x * 2 }
будут.
list.foldLeft{0}{_+_}
работает.
– Daniel C. Sobral
8 December 2010 в 12:28