#scala #arguments #default-value #currying
#scala #аргументы #значение по умолчанию #обработка
Вопрос:
У меня есть функция с двумя списками параметров, которые я пытаюсь частично применить и использовать с каррированием. Второй список параметров содержит аргументы, все из которых имеют значения по умолчанию (но не неявные). Что-то вроде этого:
def test(a: Int)(b: Int = 2, c: Int = 3) { println(a ", " b ", " c); }
Теперь все следующее в порядке:
test(1)(2, 3);
test(1)(2);
test(1)(c=3);
test(1)();
Теперь, если я определю:
def partial = test(1) _;
Затем можно выполнить следующее:
partial(2, 3);
Может кто-нибудь объяснить, почему я не могу опустить некоторые / все аргументы в ‘partial’ следующим образом:
partial(2);
partial(c=3);
partial();
Разве написание «partial» не должно вести себя по существу так же, как «test (1)»? Может кто-нибудь, пожалуйста, помочь мне найти способ достичь этого?
Пожалуйста, помогите, я в отчаянии!
РЕДАКТИРОВАТЬ — Поскольку я не могу ответить на свой собственный вопрос в течение 24 часов, я опубликую свой собственный ответ здесь:
Это лучшее, что я мог сделать сам до сих пор:
class Test2(val a: Int) {
def apply(b: Int = 2, c: Int = 3) { println(a ", " b ", " c); }
}
def test2(a: Int) = new Test2(a);
def partial2 = test2(1); // Note no underscore
test2(1)(2, 3);
test2(1)(2);
test2(1)(c=3);
test2(1)();
partial2(2, 3)
partial2(2);
partial2(c=3);
partial2();
Таким образом, это работает…
Комментарии:
Ответ №1:
Механизм вывода типов выдает partial
тип того, что будет дальше; т.е. расширение eta test(1) _
. Вы можете видеть, например, в REPL, который partial
имеет type (Int, Int) => Unit
, тогда как test
имеет type (a: Int)(b: Int,c: Int)Unit
. Результатом расширения eta является Function
объект, который не содержит никаких имен аргументов (поскольку это можно определить Function
с анонимными параметрами).
Чтобы исправить это, вы должны определить partial
следующим образом:
def partial(b: Int = 2, c: Int = 3) = test(1)(b,c)
Возможно, вы захотите выделить значения по умолчанию, где оба test
и partial
могут их достигать, чтобы убедиться, что они остаются равными. Но я не знаю ни одного трюка, позволяющего избежать повторения имен параметров без внесения дополнительных накладных расходов, таких как создание новых объектов и т.д.
Комментарии:
1.
Function
Объект действительно содержит имена аргументов, но это имена аргументов, определенные вFunction2
На самом деле вы можете вызвать пример, приведенный @Desperate следующим образом:partial(v1 = 2, v2 = 3)
.2. Я полагал, что это связано с тем, что компилятор не создает новые классы для подобных функций, а вместо этого использует «универсальную» функцию2. В противном случае он мог бы сохранить всю необходимую информацию в аннотациях. Весь смысл был в том, чтобы избежать повторения имен аргументов / списка, хотя:(
3. @Moritz, я понимаю это, но это совсем не полезно, когда кто-то хочет сохранить собственные имена. Это действительно недостаток, если не дефект языка. Я никогда не понимал, почему Scala пытается повторно использовать предварительно созданные классы Function<N> вместо того, чтобы генерировать их по мере необходимости. Я знаю, что это привело бы к созданию большего количества классов, но это было бы и правильно, и полезно в конечном итоге. Кстати, я попробую создать свой собственный функциональный объект с методом ‘apply’ в нем…
4. @Desperate Я не говорил, что это полезно — это может даже нанести вред, если ваши параметры будут иметь одинаковые имена в другом порядке. Scala на самом деле генерирует подкласс
FunctionN
случаев, подобных этому, но возврат специализированного подтипа здесь сделал бы перегрузку довольно сложной.5. Это лучшее, что я мог сделать сам на данный момент: class Test2(значение a: Int) { def apply(b: Int = 2, c: Int = 3) { println(a «, » b «, » c); } } def test2(a: Int) = new Test2(a); def partial2 = test2(1); // Обратите внимание на отсутствие подчеркивания test2(1)(2, 3); test2(1)(2); test2(1) (c = 3); test2(1)(); partial2(2, 3) partial2(2); partial2 (c = 3); partial2(); Таким образом, это работает…
Ответ №2:
Это лучшее, что я мог сделать сам до сих пор:
class Test2(val a: Int) {
def apply(b: Int = 2, c: Int = 3) { println(a ", " b ", " c); }
}
def test2(a: Int) = new Test2(a);
def partial2 = test2(1); // Note no underscore
test2(1)(2, 3);
test2(1)(2);
test2(1)(c=3);
test2(1)();
partial2(2, 3)
partial2(2);
partial2(c=3);
partial2();
Таким образом, это работает…
Ответ №3:
Следуя вашему комментарию, вот более компактный способ его написания:
def test(a: Int) = new {
def apply(b: Int = 2, c: Int = 3) {
println(a ", " b ", " c)
}
}
Это немного компактнее, чем ваше предложение, но менее эффективно, поскольку любой вызов внутреннего apply
будет происходить через отражение, как со структурными типами. На самом деле, возвращаемый тип test
является структурным типом:
java.lang.Object{def apply(b: Int,c: Int): Unit; def apply$default$1:
Int @scala.annotation.unchecked.uncheckedVariance; def apply$default$2: Int
@scala.annotation.unchecked.uncheckedVariance}