Статьи

Веселье с функцией композиции в Scala

Цель этого поста — показать, как можно составить список функций для создания одной функции в контексте сопоставления набора значений с использованием этих функций. Это симпатичный пример, демонстрирующий некоторые достоинства функционального программирования в Scala. И, хотя это не учебник, он все же может быть полезен людям, которые только начинают заниматься функциональным программированием

Начнем со списка чисел от 1 до 5 и нескольких простых функций — одна для добавления 1, другая для возведения в квадрат и третья для добавления 100.

01
02
03
04
05
06
07
08
09
10
11
scala> val foo = 1 to 5 toList
foo: List[Int] = List(1, 2, 3, 4, 5)
 
scala> val add1 = (x: Int) => x + 1
add1: (Int) => Int = <function1>
 
scala> val add100 = (x: Int) => x + 100
add100: (Int) => Int = <function1>
 
scala> val sq = (x: Int) => x * x
sq: (Int) => Int = <function1>

Затем мы можем применить любую из этих функций к каждому элементу списка foo, используя функцию map.

1
2
3
4
5
6
7
8
scala> foo map add1
res0: List[Int] = List(2, 3, 4, 5, 6)
 
scala> foo map add100
res1: List[Int] = List(101, 102, 103, 104, 105)
 
scala> foo map sq
res2: List[Int] = List(1, 4, 9, 16, 25)

Мы можем сохранить результаты сопоставления всех значений через add1, а затем отобразить результирующий список через sq.

1
2
3
4
5
scala> val bar = foo map add1
bar: List[Int] = List(2, 3, 4, 5, 6)
 
scala> bar map sq
res3: List[Int] = List(4, 9, 16, 25, 36)

Или, если мы не заботимся о промежуточном результате, мы можем просто продолжить отображение через обе функции.

1
2
scala> foo map add1 map sq
res4: List[Int] = List(4, 9, 16, 25, 36)

Выше мы сделали sq (add1 (x)) для каждого x в списке foo. Вместо этого мы могли бы составить две функции, так как sq (add1 (x)) = sq? Add1 (x). Вот как это выглядит в Scala:

1
2
3
4
5
scala> val sqComposeAdd1 = sq compose add1
sqComposeAdd1: (Int) => Int = <function1>
 
scala> foo map sqComposeAdd1
res5: List[Int] = List(4, 9, 16, 25, 36)

Конечно, мы можем сделать это с более чем двумя функциями.

1
2
3
4
5
scala> foo map add1 map sq map add100
res6: List[Int] = List(104, 109, 116, 125, 136)
 
scala> foo map (add100 compose sq compose add1)
res7: List[Int] = List(104, 109, 116, 125, 136)

И так далее. Теперь представьте, что вы хотите, чтобы пользователь программы, которую вы написали, мог выбирать функции, которые он хочет применить к списку элементов, возможно, из набора предопределенных функций, которые вы предоставили, и, возможно, те, которые они сами определяют. , Итак, вот действительно полезная часть: мы можем составить этот произвольный набор функций на лету, чтобы превратить их в единую функцию, без необходимости писать «составлять… составлять… составлять…» или «отображать… отображать… отображать… отображать» Сделайте это, создав список функций (в том порядке, в котором мы хотим применить их к значениям), а затем уменьшив их, используя функцию compose. Эквивалент тому, что мы имели выше:

1
2
3
4
5
scala> val fncs = List(add1, sq, add100)
fncs: List[(Int) => Int] = List(<function1>, <function1>, <function1>)
 
scala> foo map ( fncs.reverse reduce (_ compose _) )
res8: List[Int] = List(104, 109, 116, 125, 136)

Обратите внимание, что для правильного упорядочения композиции необходимо было перевернуть список. Если вы не хотите этого делать, вы можете использовать andThen в Scala.

1
2
scala> foo map (add1 andThen sq andThen add100)
res9: List[Int] = List(104, 109, 116, 125, 136)

Что мы, конечно, можем использовать и с помощью Reduce.

1
2
scala> foo map ( fncs reduce (_ andThen _) )
res10: List[Int] = List(104, 109, 116, 125, 136)

Поскольку функции являются гражданами первого класса (то, что мы использовали несколько раз выше), мы можем присвоить составной или andThened результат значению val и использовать его напрямую.

1
2
3
4
5
scala> val superFunction = fncs reduce (_ andThen _)
superFunction: (Int) => Int = <function1>
 
scala> foo map superFunction
res11: List[Int] = List(104, 109, 116, 125, 136)

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

Ссылка: весело с составлением функций в Scala от нашего партнера JCG Джейсона Болдриджа в блоге Bcomposes

Статьи по Теме :