Скрытые функции Scala

Позволяя r будет размером чанка, а L - начальным списком, вы можете это сделать.

chunkL = [ [i for i in L[r*k:r*(k+1)] ] for k in range(len(L)/r)] 
149
задан 3 revs, 2 users 69% 23 October 2010 в 00:11
поделиться

18 ответов

Манифесты , которые являются своего рода способом получения информации о типе во время выполнения, как если бы в Scala были овеществленные типы.

35
ответ дан 23 November 2019 в 22:21
поделиться

Экстракторы , которые позволяют заменять запутанный код стиля if-elseif-else шаблонами. Я знаю, что они не совсем скрытые , но я использую Scala в течение нескольких месяцев, не осознавая их возможности. Для (длинного) примера я могу заменить:

val code: String = ...
val ps: ProductService = ...
var p: Product = null
if (code.endsWith("=")) {
  p = ps.findCash(code.substring(0, 3)) //e.g. USD=, GBP= etc
}
else if (code.endsWith(".FWD")) {
  //e.g. GBP20090625.FWD
  p = ps.findForward(code.substring(0,3), code.substring(3, 9))
}
else {
  p = ps.lookupProductByRic(code)
}

На это, что намного , на мой взгляд,

implicit val ps: ProductService = ...
val p = code match {
  case SyntheticCodes.Cash(c) => c
  case SyntheticCodes.Forward(f) => f
  case _ => ps.lookupProductByRic(code)
}

Мне нужно немного поработать в фоновом режиме ...

object SyntheticCodes {
  // Synthetic Code for a CashProduct
  object Cash extends (CashProduct => String) {
    def apply(p: CashProduct) = p.currency.name + "="

    //EXTRACTOR
    def unapply(s: String)(implicit ps: ProductService): Option[CashProduct] = {
      if (s.endsWith("=") 
        Some(ps.findCash(s.substring(0,3))) 
      else None
    }
  }
  //Synthetic Code for a ForwardProduct
  object Forward extends (ForwardProduct => String) {
    def apply(p: ForwardProduct) = p.currency.name + p.date.toString + ".FWD"

    //EXTRACTOR
    def unapply(s: String)(implicit ps: ProductService): Option[ForwardProduct] = {
      if (s.endsWith(".FWD") 
        Some(ps.findForward(s.substring(0,3), s.substring(3, 9)) 
      else None
    }
  }

Но беготня стоит того, что отделяет часть бизнес-логики от разумного места. Я могу реализовать свои методы Product.getCode следующим образом ..

class CashProduct {
  def getCode = SyntheticCodes.Cash(this)
}

class ForwardProduct {
  def getCode = SyntheticCodes.Forward(this)     
}
39
ответ дан 23 November 2019 в 22:21
поделиться

Возможно, не слишком скрыто, но я думаю, что это полезно:

@scala.reflect.BeanProperty
var firstName:String = _

Это автоматически сгенерирует геттер и сеттер для поля, которое соответствует соглашению о bean.

Дальнейшее описание на developerworks

13
ответ дан 23 November 2019 в 22:21
поделиться

Вы можете назначить параметр вызова по имени (EDIT: это отличается от ленивого параметра!) Функции, и он не будет оцениваться, пока не будет использован функцией (EDIT: в Фактически, он будет переоцениваться каждый раз, когда он будет использоваться). См. этот часто задаваемый вопрос для получения подробной информации.

class Bar(i:Int) {
    println("constructing bar " + i)
    override def toString():String = {
        "bar with value: " + i
    }
}

// NOTE the => in the method declaration.  It indicates a lazy paramter
def foo(x: => Bar) = {
    println("foo called")
    println("bar: " + x)
}


foo(new Bar(22))

/*
prints the following:
foo called
constructing bar 22
bar with value: 22
*/
23
ответ дан 23 November 2019 в 22:21
поделиться

синтаксис заполнителя для анонимных функций

Из спецификации языка Scala:

SimpleExpr1 ::= '_'

Выражение (синтаксической категории Expr ) может содержать встроенные символы подчеркивания _ в местах, где идентификаторы допустимы. Такое выражение представляет собой анонимную функцию, в которой последующие символы подчеркивания обозначают последовательные параметры.

Из Изменения языка Scala :

_ + 1                  x => x + 1
_ * _                  (x1, x2) => x1 * x2
(_: Int) * 2           (x: Int) => x * 2
if (_) x else y        z => if (z) x else y
_.map(f)               x => x.map(f)
_.map(_ + 1)           x => x.map(y => y + 1)

Используя это, вы можете сделать что-то вроде:

def filesEnding(query: String) =
  filesMatching(_.endsWith(query))
17
ответ дан 23 November 2019 в 22:21
поделиться

Неявные определения, в частности, преобразования.

Например, предположим, что функция форматирует входную строку по размеру, заменяя середину это с "...":

def sizeBoundedString(s: String, n: Int): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

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

def sizeBoundedString[T](s: T, n: Int)(implicit toStr: T => String): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

А затем, вы можете передавать классы других типов, выполнив следующие действия:

implicit def double2String(d: Double) = d.toString

Теперь вы можете вызвать эту функцию, передав двойную:

sizeBoundedString(12345.12345D, 8)

Последний аргумент является неявным и передается автоматически из-за неявного объявления de. Кроме того, «s» обрабатывается как String внутри sizeBoundedString, потому что есть неявное преобразование из него в String.

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

sizeBoundedString(1234567890L, 8)((l : Long) => l.toString)

У вас также может быть несколько неявных аргументов, но тогда вы должны либо передать их все, либо не передавать ни один из них. Существует также сокращенный синтаксис для неявных преобразований:

def sizeBoundedString[T <% String](s: T, n: Int): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

Он используется точно так же.

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

case class Daemon(name: String) {
  def log(msg: String) = println(name+": "+msg)
}

object DefaultDaemon extends Daemon("Default")

trait Logger {
  private var logd: Option[Daemon] = None
  implicit def daemon: Daemon = logd getOrElse DefaultDaemon

  def logTo(daemon: Daemon) = 
    if (logd == None) logd = Some(daemon) 
    else throw new IllegalArgumentException

  def log(msg: String)(implicit daemon: Daemon) = daemon.log(msg)
}

class X extends Logger {
  logTo(Daemon("X Daemon"))

  def f = {
    log("f called")
    println("Stuff")
  }

  def g = {
    log("g called")(DefaultDaemon)
  }
}

class Y extends Logger {
  def f = {
    log("f called")
    println("Stuff")
  }
}

В этом примере вызов «f» в объекте Y отправит журнал демону по умолчанию, а в экземпляре X - демону X Daemon. Но вызов g в экземпляре X отправит журнал явно заданному DefaultDaemon.

Хотя этот простой пример можно переписать с перегрузкой и частным состоянием, имплициты не требуют частного состояния и могут быть помещены в контекст с помощью импорт.

16
ответ дан 23 November 2019 в 22:21
поделиться

Определения структурных типов - т. Е. Типа, описываемого методами, которые он поддерживает. Например:

object Closer {
    def using(closeable: { def close(): Unit }, f: => Unit) {
      try { 
        f
      } finally { closeable.close }
    }
}

Обратите внимание, что тип параметра closeable не определен, за исключением того, что у него есть метод close

51
ответ дан 23 November 2019 в 22:21
поделиться

Полиморфизм конструктора типов (также известный как высокодородные типы)

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

С высшими видами вы можете уловить идею любого типа , который параметризован другим типом. Конструктор типа, который принимает один параметр, называется типом (* -> *) . Например, Список . Конструктор типа, который возвращает другой конструктор типа, называется типом (* -> * -> *) . Например, Функция1 . Но в Scala у нас есть высшие типы, поэтому у нас могут быть конструкторы типов, параметризованные с помощью конструкторов других типов. Таким образом, они имеют вид ((* -> *) -> *) .

Например:

trait Functor[F[_]] {
  def fmap[A, B](f: A => B, fa: F[A]): F[B]
}

Теперь, если у вас есть Functor [List] , вы можете отображать списки. Если у вас есть Функтор [Дерево] , вы можете отображать деревья. Но что еще более важно, если у вас есть Functor [A] для любого типа A (* -> *) , вы можете отобразить функцию на A .

45
ответ дан 23 November 2019 в 22:21
поделиться

Вы можете определить свои собственные управляющие структуры. На самом деле это просто функции, объекты и некоторый синтаксический сахар, но они выглядят и ведут себя как настоящие.

Например, следующий код определяет dont {...} if (cond) и ] не {...} до (cond) :

def dont(code: => Unit) = new DontCommand(code)

class DontCommand(code: => Unit) {
  def unless(condition: => Boolean) =
    if (condition) code

  def until(condition: => Boolean) = {
    while (!condition) {}
    code
  }
}

Теперь вы можете сделать следующее:

/* This will only get executed if the condition is true */
dont {
  println("Yep, 2 really is greater than 1.")
} unless (2 > 1) 

/* Just a helper function */
var number = 0;
def nextNumber() = {
  number += 1
  println(number)
  number
}

/* This will not be printed until the condition is met. */
dont {
  println("Done counting to 5!")
} until (nextNumber() == 5) 
30
ответ дан 23 November 2019 в 22:21
поделиться

SCALA 2.8 Введенные по умолчанию и названные аргументы, которые сделали возможным добавление нового метода «копирования», который SCALA добавляет к классам Catche. Если вы определяете это:

case class Foo(a: Int, b: Int, c: Int, ... z:Int)

, и вы хотите создать новый FO, который похож на существующий FO, только с другим «n» значением, тогда вы можете просто сказать:

foo.copy(n = 3)
24
ответ дан 23 November 2019 в 22:21
поделиться

Хорошо, мне пришлось добавить еще один. Каждый объект Regex в Scala имеет экстрактор (см. Ответ от Oxbox_Lakes выше), который дает вам доступ к группам матча. Таким образом, вы можете сделать что-то вроде:

// Regex to split a date in the format Y/M/D.
val regex = "(\\d+)/(\\d+)/(\\d+)".r
val regex(year, month, day) = "2010/1/13"

Вторая строка выглядит запутанной, если вы не используете сопоставление шаблонов и экстракторов. Всякий раз, когда вы определяете val или var , что произойдет после того, что ключевое слово не просто идентификатор, а скорее шаблон. Вот почему это работает:

val (a, b, c) = (1, 3.14159, "Hello, world")

Правое выражение руки создает TUPLE3 [INT, Double, String] , которые могут сопоставить шаблон (A, B, C) .

Большую часть времени ваши паттерны используют экстракторы, которые являются членами объектов Singleton. Например, если вы пишете шаблон, как

Some(value)

, то вы неявно вызываете экстрактор некоторое время. Используйте .

Но вы также можете использовать экземпляры классов в шаблонах, и это здесь происходит. Val Regex является экземпляром Regex , и когда вы используете его в шаблоне, вы неявно вызываете Regex.unapplyseq ( unapply против unapplyseq находится за пределами объема этого ответа), который извлекает группы спичек в SEQ [строку] , элементы которых назначаются в целях переменных в году, месяц и день.

85
ответ дан 23 November 2019 в 22:21
поделиться

В scala 2.8 вы можете иметь хвостовые рекурсивные методы с помощью пакета scala.util.control.TailCalls (на самом деле это трамплин).

Пример:

def u(n:Int):TailRec[Int] = {
  if (n==0) done(1)
  else tailcall(v(n/2))
}
def v(n:Int):TailRec[Int] = {
  if (n==0) done(5)
  else tailcall(u(n-1))
}
val l=for(n<-0 to 5) yield (n,u(n).result,v(n).result)
println(l)
35
ответ дан 23 November 2019 в 22:21
поделиться

в scala 2.8 вы можете добавить @specialized к вашим общим классам/методам. Это позволит создать специальные версии класса для примитивных типов (расширение AnyVal) и сэкономить на ненужных боксах и унбоксах: class Foo[@specialized T]...

Вы можете выбрать подмножество AnyVal : class Foo[@specialized(Int,Boolean) T]...

24
ответ дан 23 November 2019 в 22:21
поделиться

Построение бесконечных структур данных с помощью Streamов Scala : http://www.codecommit.com/blog/scala/infinite-lists-for-the-finitely-patient

12
ответ дан 23 November 2019 в 22:21
поделиться

Ранняя инициализация:

trait AbstractT2 {
  println("In AbstractT2:")
  val value: Int
  val inverse = 1.0/value
  println("AbstractT2: value = "+value+", inverse = "+inverse)
}

val c2c = new {
  // Only initializations are allowed in pre-init. blocks.
  // println("In c2c:")
  val value = 10
} with AbstractT2

println("c2c.value = "+c2c.value+", inverse = "+c2c.inverse)

Выход:

In AbstractT2:  
AbstractT2: value = 10, inverse = 0.1  
c2c.value = 10, inverse = 0.1

Мы инстанцируем анонимный внутренний класс, инициализируя поле value в блоке, перед с AbstractT2. Это гарантирует что value инициализируется перед перед выполнением тела AbstractT2, как как показано при запуске сценария.

17
ответ дан 23 November 2019 в 22:21
поделиться

@switch в Scala 2.8:

Применяемая аннотация на матч выражение. Если присутствует, компилятор проверит, что совпадение было скомпилирован в tablewitch или lookupswitch и выдает ошибку, если он вместо этого компилируется в серию условные выражения.

Пример:

scala> val n = 3
n: Int = 3

scala> import annotation.switch
import annotation.switch

scala> val s = (n: @switch) match {
     |   case 3 => "Three"
     |   case _ => "NoThree"
     | }
<console>:6: error: could not emit switch for @switch annotated match
       val s = (n: @switch) match {
26
ответ дан 23 November 2019 в 22:21
поделиться

Типы результатов зависят от неявного разрешения. Это может дать вам форму множественной отправки:

scala> trait PerformFunc[A,B] { def perform(a : A) : B }
defined trait PerformFunc

scala> implicit val stringToInt = new PerformFunc[String,Int] {
  def perform(a : String)  = 5
}
stringToInt: java.lang.Object with PerformFunc[String,Int] = $anon$1@13ccf137

scala> implicit val intToDouble = new PerformFunc[Int,Double] {
  def perform(a : Int) = 1.0
}
intToDouble: java.lang.Object with PerformFunc[Int,Double] = $anon$1@74e551a4

scala> def foo[A, B](x : A)(implicit z : PerformFunc[A,B]) : B = z.perform(x)
foo: [A,B](x: A)(implicit z: PerformFunc[A,B])B

scala> foo("HAI")
res16: Int = 5

scala> foo(1)
res17: Double = 1.0
12
ответ дан 23 November 2019 в 22:21
поделиться

Вы можете использовать локально , чтобы ввести локальный блок, не вызывая проблем с выводом точки с запятой.

Использование:

scala> case class Dog(name: String) {
     |   def bark() {
     |     println("Bow Vow")
     |   }
     | }
defined class Dog

scala> val d = Dog("Barnie")
d: Dog = Dog(Barnie)

scala> locally {
     |   import d._
     |   bark()
     |   bark()
     | }
Bow Vow
Bow Vow

локально определено в «Predef.scala» как:

@inline def locally[T](x: T): T = x

Будучи встроенным, он не накладывает никаких дополнительных накладных расходов.

20
ответ дан 23 November 2019 в 22:21
поделиться