Реализация def с N параметрами в качестве значения типа FunctionN - PullRequest
1 голос
/ 28 января 2010

Я могу реализовать def с val, где def не принимает аргументов:

trait T { def foo: Int }
class C(val foo: Int) extends T

Почему это нельзя распространить на реализацию def, переводящего N аргументов в значение, равное FunctionN? Я хочу, чтобы можно было реализовать что-то вроде:

def expensiveOperation(p: Int => Boolean) : List[Int]

С ленивой функцией val. Что-то вроде:

val expensiveOperation = {
    val l = //get expensive list
    l.filter _ //partially applied function
}

Я знаю, что этот синтаксис не работает на 2.8. Есть ли что-то, чего мне не хватает, почему я не могу реализовать a def, принимая параметры как функционал val?

Ответы [ 4 ]

2 голосов
/ 28 января 2010

A val не принимает аргументов, потому что он вычисляется и сохраняется в поле. Но я мог бы просто отослать вас к обширным постам, которые я делал последние два дня в списках рассылки.

Или, скорее, давайте рассмотрим это с точки зрения Java, поскольку Scala совместима с Java на уровне jvm и, безусловно, должна подчиняться правилам jvm. Начнем с первого класса:

abstract class X {
  def expensiveOperation(p: Int => Boolean) : List[Int] 
}

Теперь давайте расширим его:

abstract class Y extends X {
  override val expensiveOperation: ((Int) => Boolean) => List[Int]
}

Итак, из Java мы знаем, что класс X имеет метод expensiveOperation, который получает Function1[Int, Boolean] и возвращает List[Int].

Теперь мы идем в класс Y. Естественно, он должен определять тот же метод, но он также должен определять метод получения expensiveOperation, который не получает аргументов и возвращает Function1[Function1[Int, Boolean],List[Int]].

Это может быть работоспособным, если этот дополнительный метод не существует и в X. Итак, давайте определим это:

class Z extends Y {
  override val extensiveOperation = new Function1[Function1[Int, Boolean], List[Int]] {
    def apply(p: Int => Boolean) = List range (1, 10) filter p
  }
}

Как это определяется? Копирует ли Scala тело apply как тело для expensiveOperation (получающего параметр, а не получающего)? Это все еще может быть работоспособным. Давайте попробуем что-то еще, хотя:

class W(f: ((Int) => Boolean) => List[Int]) extends Y {
  override val extensiveOperation = f
}

Теперь, как мы можем переопределить получение параметра extensiveOperation? Я полагаю, мы могли бы написать это так:

override def extensiveOperation(p: Int => Boolean) = extensiveOperation.apply(p)

Это выполнимо. Но я лично считаю это немного запутанным. Мое предложение: напишите короткий SID и получите соглашение по списку рассылки Scala. Однако без кода для его реализации я не думаю, что у него есть много шансов быть принятым - Scala должна отслеживать каждую типизированную val, чтобы определить, переопределяет ли она def или нет.

2 голосов
/ 28 января 2010

Теперь, после редактирования, я думаю, я понимаю, что вы после. Но вы не можете делать то, что хотите, потому что подписи типов не совпадают.

def x: Int = 5
val x: Int = 5

В обоих случаях вы ничего не предоставляете и получаете Int (в данном случае 5). Отлично!

def expensive(p: Int => Boolean): List[Int]

Теперь вы что-то поставляете. Но val - это просто сохраненный объект где-то. Вы можете указать что-то для объекта, на который ссылается метка , но это не то же самое, что указать что-то для метки 'x'.

Вот определение, которое вам нужно использовать, если вы хотите, чтобы val переопределял его:

def expensive: (Int=>Boolean)=>List[Int]

Теперь у вас есть то, что вы вызываете без параметров, и оно возвращает что-то, что может принять булеву функцию Int => и вернуть вам List [Int]. Это именно то, что вы получаете с val - в обоих случаях у вас есть имя чего-то, что возвращает объект, обладающий нужной вам функциональностью.

(В Scala val фактически реализуются как скрытые поля с методами-получателями, которые не принимают параметров и возвращают все, что находится в скрытом поле. Так что это действительно метод, такой же, как и у def.)

1 голос
/ 28 января 2010

Вы всегда можете просто переслать его на ваш val:

trait T {
  def expensiveOperation(p: Int => Boolean) : List[Int]
}

class C extends T {
  def expensiveOperation(p: Int => Boolean): List[Int] = {
      expensiveOperationVal(p) 
  }
  val expensiveOperationVal = { p: (Int=>Boolean) =>
    // ... lazy stuff
    List(1,2,3)
  }
}

И, хотя это не отвечает на ваш вопрос, похоже, если ваш код // ... get expensive list не зависит от предиката p, тогда вы можете просто сделать что-то похожее на:

* +1007 *
0 голосов
/ 28 января 2010

Редактировать: хорошо, я не понял, что вы на самом деле имели в виду. Но если бы вы имели в виду то, что я думал ...

Вы можете, используя сокращенный синтаксис, создать val, который применяет операцию:

val expensive = (p: (Int) => Boolean) => {
  val l = List(1,2,3,4)
  l filter p
}
scala> expensive(_<3)
res1: List[Int] = List(1,2)

Но это на самом деле не кеширует список, что, я думаю, вам нужно. Причина в том, что этот сокращенный синтаксис помещает все после => в метод apply функции Function1. Вы, вероятно, хотите, чтобы список сохранялся примерно так:

* +1007 *

и для этого я не знаю коротких стенографий.

Редактировать: Если вы счастливы создать список за пределами этого блока, то есть сокращение (см. Также комментарий):

val expensive = List(1,2,3,4).filter _

scala> expensive(_ < 3)
res6: List[Int] = List(1, 2)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...