Медленное утверждение Scala

Недавно мы профилировали наш код и натолкнулись на несколько раздражающих горячих точек. Они в форме

assert(a == b, a + " is not equal to " + b)

Поскольку некоторые из этих утверждений могут быть вызваны в коде огромное количество раз, совпадение строк начинает складываться. assert определяется как:

def assert(assumption : Boolean, message : Any) = ....

почему это не определяется как:

def assert(assumption : Boolean, message : => Any) = ....

Так будет лениво оценивать. Учитывая, что это не определено таким образом, существует ли встроенный способ вызова assert с параметром сообщения, который вычисляется лениво?

Спасибо


person David    schedule 11.03.2010    source источник
comment
Для тех, кто не знает, это было исправлено в 2.8.   -  person David    schedule 16.09.2010


Ответы (4)


Ленивое вычисление также имеет некоторые накладные расходы для созданного функционального объекта. Если ваш объект сообщения уже полностью сконструирован (статическое сообщение), в этих накладных расходах нет необходимости.

Подходящим методом для вашего варианта использования будет sprintf-style:

assert(a == b,  "%s is not equal to %s", a, b)

Пока есть специальная функция

assert(Boolean, String, Any, Any)

эта реализация не имеет накладных расходов или стоимости массива var args

assert(Boolean, String, Any*)

для общего случая.

Реализация toString будет оцениваться лениво, но не читается:

assert(a == b, new { override def toString =  a + " is not equal to " + b })
person Thomas Jung    schedule 11.03.2010
comment
Спасибо, Томас. Я не учел накладных расходов на ленивую оценку. Исходя из нашего кода, накладные расходы на ленивую оценку намного меньше, чем при использовании конкатенации строк, поэтому я думаю, что мы продолжим писать наш собственный метод assert. Это некрасиво, но сильно ускоряет работу. - person David; 11.03.2010

Это по имени, я поменял его больше года назад.

http://www.scala-lang.org/node/825

Текущий Predef:

@elidable(ASSERTION)
def assert(assertion: Boolean, message: => Any) {
  if (!assertion)
    throw new java.lang.AssertionError("assertion failed: "+ message)
}
person psp    schedule 13.03.2010
comment
@extempore: Это только для 2.8? Или 2,7 тоже? - person Randall Schulz; 13.03.2010

Ответ Томаса отличный, но на всякий случай, если вам нравится идея последнего ответа, но не нравится его нечитабельность, вы можете обойти это:

object LazyS {
  def apply(f: => String): AnyRef = new {
    override def toString = f
  }
}

Пример:

object KnightSpeak {
  override def toString = { println("Turned into a string") ; "Ni" }
}

scala> assert(true != false , LazyS("I say " + KnightSpeak))

scala> println( LazyS("I say " + KnightSpeak) )
Turned into a string
I say Ni
person Rex Kerr    schedule 12.03.2010

Попробуйте: assert( a==b, "%s is not equals to %s".format(a,b)) Формат следует вызывать только тогда, когда для утверждения требуется строка. Формат добавляется к RichString неявно.

person Jim Barrows    schedule 11.03.2010
comment
Это неправильно именно по той причине, которую выделил вопрошающий: метод assert принимает Any, а не => Any - person oxbow_lakes; 11.03.2010
comment
Я почти уверен, что в формате нет магии, поэтому он будет просто вызываться как обычно, независимо от того, истинно a == b или нет. - person David; 11.03.2010
comment
д'ох. Я id10t. Теперь это очевидно. - person Jim Barrows; 12.03.2010