Обработка Scala и аргументы по умолчанию

#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. permalink.gmane.org/gmane.comp.lang.scala.user/36288

Ответ №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}