Повторяющиеся параметры по имени

Как передать повторяющиеся параметры по имени в Scala?

Следующий код не работает:

scala> def foo(s: (=> String)*) = {
<console>:1: error: no by-name parameter type allowed here
       def foo(s: (=> String)*) = {
                   ^

Есть ли другой способ передать методу переменное количество параметров по имени?


person Green Hyena    schedule 25.04.2010    source источник
comment
Дубликат: stackoverflow.com/questions/2618891 /   -  person retronym    schedule 25.04.2010
comment
По какой причине вы хотите использовать параметры по имени в этом случае? Возможно, мы сможем предложить лучшее решение.   -  person Randall Schulz    schedule 26.04.2010


Ответы (5)


Это не очень красиво, но позволяет передавать параметры по имени в стиле varargs

def printAndReturn(s: String) = {
  println(s)
  s
}

def foo(s: (Unit => String)*) {
  println("\nIn foo")
  s foreach {_()}  // Or whatever you want ...
}

foo()

foo((Unit) => printAndReturn("f1"),
    (Unit) => printAndReturn("f2"))

Это производит

в фу

В фу f1 f2

person Don Mackenzie    schedule 26.04.2010
comment
Смотреть и писать, конечно, неприятно. И я продолжаю призывать людей различать параметры по имени (которые реализованы с помощью thunk, но в остальном логически различны) и значения функций, используемые в качестве аргументов. Аналогично для методов и функций. - person Randall Schulz; 26.04.2010
comment
Я думаю, что комментарии Рэндалла справедливы, вышеприведенное, возможно, можно было бы улучшить с помощью вспомогательной функции для переноса/предварительной обработки аргументов, например: def deferred(block: => String) = () => block с foo, измененным на: def foo(s: (() => String)*) дает, например: foo(deferred {val a = apples; Green +apples}, deferred {Pears}) - person Don Mackenzie; 03.05.2010

Повторяющиеся параметры по имени в настоящее время не поддерживаются.

person Randall Schulz    schedule 25.04.2010

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

implicit def arg2func(arg: ⇒ Byname): (() => Byname) = () ⇒ arg

def foo(params: (() ⇒ Byname)*): Unit = {
    println("foo: not yet evaluated.")
    params.foreach(_())
}

def boo(params: Byname*): Unit = {
    println("boo: already evaluated")
}

foo(Byname(0), Byname(1), Byname(2))
println()
boo(Byname(3), Byname(4), Byname(5))

case class Byname(n: Int) {
    println(n)
}

Это печатает:

foo: еще не оценено.

0 1 2

3 4 5

бу: уже оценено

person Markus Marvell    schedule 19.12.2015

Вы можете написать () => String вместо Unit (в любом случае это одно и то же)

person Vlad Patryshev    schedule 14.07.2012
comment
Это действительно не то же самое. Семантика и синтаксис отличаются как для вызова, так и для их использования в методе. - person Randall Schulz; 31.03.2013

Спасибо Рэндаллу Шульцу за хороший однострочный ответ.

Я искал эту возможность, чтобы создать инструмент INVARIANT, который запускал бы несколько утверждений вместе. Решение, которое я тогда придумал, состоит в том, чтобы просто иметь 1..5 apply методов, поскольку количество необходимых здесь varargs конечно.

object INVARIANT {
  def apply = {}
  def apply( conds: => Boolean * ) = {    // DOES NOT COMPILE
    conds.foreach( assert(_) )
  }
}

object TestX extends App {

  class A {
    println("A body")
    INVARIANT( true )
  }

  class B extends A {
    println("B body")
    INVARIANT( true, false )  
  }

  new B
}

Я опубликовал это, чтобы показать, что я считаю допустимым вариантом использования varargs для переменных «по имени». Если есть лучшее имя, пожалуйста, оставьте комментарий. Спасибо.

person akauppi    schedule 31.03.2013