Каков механизм, с помощью которого функции с несколькими списками параметров могут (иногда) использоваться с меньшим количеством параметров, чем требуется?

Позвольте мне представить этот вопрос в качестве примера. Это было взято из лекции 2.3 курса Мартина Одерского по функциональному программированию.

У меня есть функция для итеративного поиска фиксированных точек, например

 object fixed_points {
  println("Welcome to Fixed Points")              
  val tolerance = 0.0001                          
  def isCloseEnough(x: Double, y: Double) =
    abs((x-y)/x) < tolerance                  

  def fixedPoint(f: Double => Double)(firstGuess: Double) = {
    def iterate(guess: Double): Double = {
        println(guess)
        val next = f(guess)
        if (isCloseEnough(guess, next)) next
        else iterate(next)
    }
    iterate(firstGuess)
  }

Я могу адаптировать эту функцию для поиска квадратных корней, например так

  def sqrt(x: Double) =
  fixedPoint(y => x/y)(1.0) 

Однако это не сходится для определенных аргументов (например, 4). Поэтому я применяю к нему среднее демпфирование, по сути, преобразовывая его в Ньютон-Рафсон, вот так

  def sqrt(x: Double) =
  fixedPoint(y => (x/y+y)/2)(1.0) 

который сходится.

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

 def averageDamp(f: Double => Double)(x: Double) = (x+f(x))/2

и

  def sqrtDamp(x: Double) =
    fixedPoint(averageDamp(y=>x/y))(1.0)              (*)

Вау! Что только что произошло?? Я использую averageDamp только с одним параметром (когда он был определен с двумя), и компилятор не жалуется!

Теперь я понимаю, что могу использовать частичное приложение так

 def a = averageDamp(x=>2*x)_                   
   a(3)  // returns 4.5

Там нет проблем. Но когда я пытаюсь использовать averageDamp с меньшим количеством параметров, чем необходимо (как это было сделано в sqrtDamp), вот так

 def a = averageDamp(x=>2*x)                (**)

Я получаю сообщение об ошибке missing arguments for method averageDamp.

Вопросы:

  1. Чем то, что я сделал в (**), отличается от (*), на что компилятор жалуется в первом, но не во втором?
  2. Таким образом, похоже, что при определенных обстоятельствах допускается использование меньшего количества необходимых параметров. Что это за обстоятельства и как называется этот механизм? (Я понимаю, что это подпадает под тему «каррирования», но я как бы ищу конкретное название этого подмножества каррирования)

person thetrystero    schedule 25.11.2015    source источник
comment
fixedPoint принимает функцию, поэтому она готова расширить ваш аргумент. Другое использование не имеет ожидаемого типа, поэтому не будет. Это часто задаваемые вопросы.   -  person som-snytt    schedule 25.11.2015


Ответы (1)


Этот ответ расширяет комментарий, опубликованный @som-snytt.

Разница между (**) и (*) заключается в том, что в первом fixedPoint предоставляет определение типа, а во втором a — нет. По сути, всякий раз, когда ваш код предоставляет явное объявление типа, компилятор будет счастлив, если вы пропустите знак подчеркивания в конце. Это преднамеренное дизайнерское решение, см. объяснение Мартина Одерски.

Чтобы проиллюстрировать это положение, вот небольшой пример.

object A {
  def add(a: Int)(b:Int): Int = a + b
  val x: Int => Int = add(5) // compiles fine
  val y = add(5) // produces the following compiler error
}
/* missing arguments for method add in object A;
   follow this method with `_' if you want to treat it as a partially applied function
   val y = add(5)
              ^
*/
person moem    schedule 25.11.2015