package com.nealford.conf.ft.numberclassifier object NumberClassifier { def isFactor(number: Int, potentialFactor: Int) = number % potentialFactor == 0 def factors(number: Int) = (1 to number) filter (number % _ == 0) def sum(factors: Seq[Int]) = factors.foldLeft(0)(_ + _) def isPerfect(number: Int) = sum(factors(number)) - number == number def isAbundant(number: Int) = sum(factors(number)) - number > number def isDeficient(number: Int) = sum(factors(number)) - number < number }
factors()
and sum()
. The factors()
method takes a list of numbers from 1 to a given number and applies the standard Scala filter()
library method to it, using the block of code on the right as a filtering criterion (otherwise called a predicate). In the code block, a special construction is used - implicit parameters * , which allows using the _
symbol instead of the parameter name when there is no need for it. Thanks to the flexibility of the Scala syntax, you can call the filter()
method as well as the operator. The construction (1 to number).filter((number % _ == 0))
will also work if you find it more convenient.
sum
method uses the left-folding operation already familiar to us (fold left) (implemented in Scala as the foldLeft()
method). I don't need names for the parameters in this case, so I use the _
symbol instead of the name, which gives me a simpler and more understandable syntax for the code block. The foldLeft()
method does the same as the similarly named method from the Functional Java library, which appeared in the first part:
isPerfect()
method from Listing 1:
@Test def negative_perfection() { for (i <- 1 until 10000) if (Set(6, 28, 496, 8128).contains(i)) assertTrue(NumberClassifier.isPerfect(i)) else assertFalse(NumberClassifier.isPerfect(i)) }
if
is not really important to me. What problem am I trying to solve? I have to be sure that my classifier does not define the imperfect number as perfect. Listing 3 shows a solution to this problem, but in a slightly different formulation.
@Test def alternate_perfection() { assertEquals(List(6, 28, 496, 8128), (1 until 10000) filter (NumberClassifier.isPerfect(_))) }
filter()
method in Listing 3) shows a “different” approach to reuse. If you come from the old, design-defining patterns of an object-oriented world, compare this approach with the Pattern Method pattern from the Design Patterns book ** . It defines the algorithm's procurement in the base class, using abstract methods in order to postpone the definition of details until the creation of successors. The functional approach allows you to pass operations as parameters, and apply them appropriately within a method.
def product = { x, y -> return x * y } def quadrate = product.curry(4) def octate = product.curry(8) println "4x4: ${quadrate.call(4)}" println "5x8: ${octate(5)}
product
as a block of code that takes two parameters. Using the built-in groovy curry()
method, I use product
as the base element to create two new quadrate
and octate
code blocks. Groovy makes calling code very simple: you can either explicitly call the call () method, or use the syntax sugar provided by the language — a couple of brackets.
curry()
method to implement both techniques as shown in Listing 5:
def volume = { h, w, l -> return h * w * l } def area = volume.curry(1) def lengthPA = volume.curry(1, 1) //partial application def lengthC = volume.curry(1).curry(1) // currying println "The volume of the 2x3x4 rectangular solid is ${volume(2, 3, 4)}" println "The area of the 3x4 rectangle is ${area(3, 4)}" println "The length of the 6 line is ${lengthPA(6)}" println "The length of the 6 line via curried function is ${lengthC(6)}"
area
code (which calculates the area of ​​a rectangle) fixing the first volume
argument to 1. To use volume as the basis for the block that returns the length of the line, I can use both partial application and currying. lengthPA
uses partial application by fixing the first two parameters on the value 1. lengthC
applies currying twice to get the same result. The difference is very subtle, and the results are the same, however, if you use the terms currying and partial use as synonyms next to a real functional programmer, you will most likely be shown an error *** .
def composite = { f, g, x -> return f(g(x)) } def thirtyTwoer = composite.curry(quadrate, octate) println "composition of curried functions yields ${thirtyTwoer(2)}"
composite
code block that combines two functions. Using this block, I create thirtyTwoer
using partial applications in order to combine the two methods together.
incrementer
code block by simply completing the adder
block as shown in Listing 7:
def adder = { x, y -> return x + y } def incrementer = adder.curry(1) println "increment 7: ${incrementer(7)}"
object CurryTest extends Application { def filter(xs: List[Int], p: Int => Boolean): List[Int] = if (xs.isEmpty) xs else if (p(xs.head)) xs.head :: filter(xs.tail, p) else filter(xs.tail, p) def dividesBy(n: Int)(x: Int) = ((x % n) == 0) val nums = List(1, 2, 3, 4, 5, 6, 7, 8) println(filter(nums, dividesBy(2))) println(filter(nums, dividesBy(3))) }
dividesBy()
method using the filter()
method. I pass the anonymous function to the filter()
method, applying the currying method to fix the first dividesBy()
parameter on the value used to create this block. When I pass the number to the dividesBy()
method, Scala automatically creates a new function using currying.
def filter(list, criteria) { def new_list = [] list.each { i -> if (criteria(i)) new_list << i } return new_list } modBy2 = { n -> n % 2 == 0 } l = filter(1..20, modBy2) println l
filter()
method in Listing 9 accepts a list and a selection criterion (code block) as input. Inside it moves through the list by adding the next item to the new list if it meets the criteria.
filter()
method first checks whether the list is empty - this is a necessary condition for exiting recursion. If the list is empty, then we simply exit the method; if not, we calculate the value of the condition passed as a parameter. If this value is satisfied (which means that this item is to be seen in the list at the output), we return a new list consisting of this element and a filtered list of the remaining elements. If the condition is not fulfilled, then we return the filtered list of the remaining elements (excluding the head). The Scala language list creation operator provides us with good readability and easy perception of both cases.
_
construct instead of the name of the bound variable in the closure is called the placeholder syntax for anonymous function (Placeholder Syntax for Anonymous Functions), and not the implicit parameter . The implicit parameter is entered by the keyword implicit
in the function signature and has a completely different semantics.
Source: https://habr.com/ru/post/132694/