Как написать f(g(h(x))) на Scala с меньшим количеством скобок?

Такие выражения, как

ls map (_ + 1) sum

прекрасны, потому что они расположены слева направо и не вложены друг в друга. Но если рассматриваемые функции определены вне класса, это менее красиво.

Следуя примеру, я попробовал

final class DoublePlus(val self: Double) {
    def hypot(x: Double) = sqrt(self*self + x*x)
}

implicit def doubleToDoublePlus(x: Double) =
    new DoublePlus(x)

который отлично работает, насколько я могу судить, кроме

  1. Много печатать для одного метода

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

Есть ли трюк, который решит эти две проблемы?


person Owen    schedule 29.08.2011    source источник
comment
Что плохого в том, чтобы определить гипот в служебном объекте, а затем использовать его как hypot(3,4)? Кажется, лучший выбор здесь.   -  person IttayD    schedule 29.08.2011


Ответы (4)


Вы можете вызвать andThen для функционального объекта:

(h andThen g andThen f)(x)

Однако вы не можете напрямую вызывать его в методах, поэтому, возможно, ваш h должен стать (h _), чтобы преобразовать метод в частично применяемую функцию. Компилятор автоматически преобразует имена последующих методов в функции, поскольку метод andThen принимает параметр Function.

Вы также можете использовать оператор канала |>, чтобы написать что-то вроде этого:

x |> h |> g |> f
person Jean-Philippe Pellet    schedule 29.08.2011

Обогащение существующего класса/интерфейса неявным преобразованием (что вы сделали с doubleToDoublePlus) — это все, что касается дизайна API, когда некоторые классы не находятся под вашим контролем. Я не рекомендую делать это слегка, просто чтобы сэкономить несколько нажатий клавиш или иметь несколько меньше скобок. Поэтому, если важно иметь возможность набирать val h = d hypot x, дополнительные нажатия клавиш не должны вызывать беспокойства. (могут быть проблемы с размещением объектов, но это другое).

Название и ваш пример также не совпадают:

f(g(h(x))) можно переписать как f _ compose g _ compose h _ apply x, если вас беспокоят круглые скобки, или f compose g compose h apply x, если f, g, h являются функциональными объектами, а не def.

Но ls map (_ + 1) sum не являются вложенными вызовами, как вы говорите, поэтому я не уверен, как это связано с заголовком. И хотя им прекрасно пользоваться, разработчики библиотек/языков приложили много усилий, чтобы сделать его простым в использовании, а внутри не так просто (гораздо сложнее, чем ваш hypot пример).

person huynhjl    schedule 29.08.2011
comment
Ну, они являются вложенными вызовами в некотором смысле: результат каждого шага становится аргументом this для следующего вызова. В других языках это вложение было бы явным. Моя проблема заключается в том, что хотя вопрос о том, является ли конкретный аргумент аргументом this, функционально не очень важен, только из-за этого факта вы получаете хороший синтаксис вызова. - person Owen; 29.08.2011

Может быть, это, посмотрите, как предоставляется:

def compose[A, B, C](f: B => C, g: A => B): A => C = (a: A) => f(g(a))

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

person AndreasScheinert    schedule 29.08.2011

Начиная с Scala 2.13, стандартная библиотека предоставляет операцию объединения pipe, который можно использовать для преобразования/передачи значения с интересующей функцией.

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

import scala.util.chaining._

x pipe h pipe g pipe f
person Xavier Guihot    schedule 03.10.2018