Im trying to write a function using currying to compute a Collatz series... But im getting lost when trying to write the parameters for construct6(...)
def c: Int => Int = construct6(isEven)(dec, inc)
//DONT KNOW WHAT TO DO HERE
def construct6: ((Int => Boolean) => (Int => Int, Int => Int)) => (i => (e, o)) = {
if (i(n)) e(n) else o(n)
}
def isEven: (Int) => (Boolean) = (a) => (a % 2 == 0)
def dec: (Int) => (Int) = (a) => (a / 2)
def inc: (Int) => (Int) = (a) => (a * 3 + 1)
You're construct6 function MUST take a Int n. Here is the function curried 3 times...
def construct6(i: Int => Boolean)(e: Int => Int, o: Int => Int)( n: Int): Int =
if (i(n)) e(n) else o(n)
When dealing with problems like this it is always important to look at the type signatures and see that they match up. Notice that construct6 MUST return an Int. We can doing some functional magic to make a function with a type signature to match Int => Int by doing this:
val c: Int => Int = construct6(isEven)(dec, inc)(_: Int)
We are partially applying the function you want for i, e, o, and leaving n as a variable.
Given:
def isEven: (Int) => (Boolean) = (a) => (a % 2 == 0)
def dec: (Int) => (Int) = (a) => (a / 2)
def inc: (Int) => (Int) = (a) => (a * 3 + 1)
you are still missing the n parameter to construct6 (as Andrew Cassidy says). Since you need to curry its parameters, it's more readable if you define construct6 like this (but it's basically the same definition you gave, I just added the n:Int at the end):
def construct6 (i:Int => Boolean)(e:(Int => Int)) (o:(Int => Int))(n:Int)
= if (i(n)) e(n) else o(n)
you can now define c as a Int=>Int by leaving out the last parameter n to construct6. In the REPL:
scala> def c: Int => Int = construct6(isEven)(dec)(inc)
c: Int => Int
scala> c(8)
res0: Int = 4
which I believe is what you were trying to do.
If you want to infer c's type instead of specifying it as a Int => Int explicitly) you will have to use the omnipresent _ to confirm to Scala that you're not just leaving out anything by mistake:
scala> def c2= construct6(isEven)(dec)(inc) _
c2: Int => Int
scala> c2(8)
res1: Int = 4
Related
def fact(n: Int) = products(x => x)(1,n)
fact(5)
def mapReduce(f: Int => Int, combine: (Int, Int) => Int, zero: Int)(a:Int,b:Int): Int =
def recur(a: Int): Int =
if(a>b) zero
else combine(f(a),recur(a+1))
recur(a)
def sum(f: Int => Int) = mapReduce(f, (x, y) => x+y, 0)
def products(f: Int => Int) = mapReduce(f, (x, y) => x*y, 1)
sum(fact)(1,5)
products(fact)(1,5)
def mapReduce(f: Int => Int, combine: (Int, Int) => Int, zero: Int)(a:Int,b:Int): Int = {
def recur(a: Int): Int =
if (a > b) zero
else combine(f(a), recur(a + 1))
recur(a)
}
def sum(f: Int => Int): (Int, Int) => Int = mapReduce(f, (x, y) => x+y, 0)
def products(f: Int => Int): (Int, Int) => Int = mapReduce(f, (x, y) => x*y, 1)
def fact(n: Int) = products(x => x)(1,n)
fact(5)
sum(fact)(1,5)
products(fact)(1,5)
I tried it using scala 2.13 on a scala worksheet, here curly brackets are needed for mapReduce as it contains two expressions one is a function definition and another a function invocation. Secondly when methods that returns functions such as sum and products the return type need to be specified. But I wasn't able to reproduce the error that you mentioned.
What is the logical reason that the first form works and not the second?
scala> val d = (a: Int, b: Int) => a + b
d: (Int, Int) => Int = <function2>
scala> val d = (a: Int)(b: Int) => a + b
<console>:1: error: not a legal formal parameter.
Note: Tuples cannot be directly destructured in method or function parameters.
Either create a single parameter accepting the Tuple1,
or consider a pattern matching anonymous function: `{ case (param1, param1) => ... }
val d=(a:Int)(b:Int)=>a+b
Because multiple parameter lists aren't allowed on function declarations. If you want to curry a function, you do:
scala> val d: Int => Int => Int = a => b => a + b
d: Int => (Int => Int) = $$Lambda$1106/512934838#6ef4cbe1
scala> val f = d(3)
f: Int => Int = $$Lambda$1109/1933965693#7e2c6702
scala> f(4)
res6: Int = 7
You can also create a single parameter list and partially apply it:
scala> val d = (a: Int, b: Int) => a + b
d: (Int, Int) => Int = $$Lambda$1064/586164630#7c8874ef
scala> d(4, _: Int)
res2: Int => Int = $$Lambda$1079/2135563436#4a1a412e
We partially applied d with 4, and we got back a function, Int => Int, which means when we supply the next argument, we'll get the result:
scala> res2(3)
res3: Int = 7
We can also create a named method, and use eta-expansion to create a curried function out of it:
scala> def add(i: Int)(j: Int): Int = i + j
add: (i: Int)(j: Int)Int
scala> val curriedAdd = add _
curriedAdd: Int => (Int => Int) = $$Lambda$1115/287609100#f849027
scala> val onlyOneArgumentLeft = curriedAdd(1)
onlyOneArgumentLeft: Int => Int = $$Lambda$1116/1700143613#77e9dca8
scala> onlyOneArgumentLeft(2)
res8: Int = 3
Function currying is possible.
val curryFunc = (a: Int) => (b: Int) => a + b
curryFunc now has the type Int => (Int => Int)
I wrote a quite strange method in Scala.
def mystery(p: Int => String => Boolean): Int = ???
And now I cannot figure out what implementation and parameter value it should have.
So what is the simplest implementation this method can have? And what is the value we can pass to it?
My try:
def mystery(p: Int => String => Boolean): Int = {
val m = (x1: Int) => p(x1)
val n = (x2: String) => m(_)(x2)
val k = (x3: Boolean) => p(_)(_)(x3)
if (k) 1 else 0
}
How about this? p is a function, so to use it you need to pass it parameters. The parameters are curried, and the function is equivalent to Int => (String => Boolean). You give it an Int first, and it returns a function String => Boolean. So you give that a String, and it returns a Boolean.
def mystery(p: Int => String => Boolean): Int = {
val a = p(1)
val b = a("2")
if(b) 1 else 0
}
val f: Int => String => Boolean = (i: Int) => (s: String) => i.toString == s
mystery(f)
In your version, m, n, and k are all defined as functions. So, for example, when you say if (k) it doesn't make sense because if takes a Boolean, but you're giving it a function.
Is it possible to do something like the following?
def takeCurriedFnAsArg(f: (Int)(implicit MyClass) => Result)
Yes, it is possible.
When you have the second curried parameter marked as implicit, the function seems to be not of type
Int => (MyClass => Result) => ResultOfFunction
which it would be if the curried higher order function parameter was a regular parameter; instead, it looks like this:
Int => ResultOfFunction
Here's a quick example:
scala> def curriedFn(i : Int)(implicit func : String => Int) : Boolean = (i + func("test!")) % 2 == 0
curriedFn: (i: Int)(implicit func: String => Int)Boolean
scala> implicit val fn : String => Int = s => s.length
fn: String => Int = <function1>
scala> curriedFn _
res4: Int => Boolean = <function1>
As you can see, the implicit parameter got 'eliminated'. Why and how? That's a question for someone more knowledgeable than me. If I had to guess, I'd say the compiler directly substitutes the parameter with the implicit value, but that might very well be false.
Anyway, digressions aside, here's an example very relevant to your situation:
scala> def foo(func : Int => Boolean) = if(func(3)) "True!" else "False!"
foo: (func: Int => Boolean)String
scala> foo(curriedFn)
res2: String = True!
Now if the second function parameter wasn't implicit:
scala> def curriedNonImplicit(i : Int)(fn : String => Int) : Boolean = (i + fn("test!")) % 2 == 0
curriedNonImplicit: (i: Int)(fn: String => Int)Boolean
scala> curriedNonImplicit _
res5: Int => ((String => Int) => Boolean) = <function1>
As you can see, the type of the function is a bit different. That means that the solution will look different too:
scala> def baz(func : Int => (String => Int) => Boolean) = if(func(3)(s => s.length)) "True!" else "False!"
baz: (func: Int => ((String => Int) => Boolean))String
scala> baz(curriedNonImplicit)
res6: String = True!
You have to specify the function directly inside the method, as it wasn't implicitly provided before.
If I have:
val f : A => B => C
This is shorthand for:
val f : Function1[A, Function1[B, C]]
How do I get a function g with the signature:
val g : (A, B) => C = error("todo")
(i.e.)
val g : Function2[A, B, C] //or possibly
val g : Function1[(A, B), C]
in terms of f?
scala> val f : Int => Int => Int = a => b => a + b
f: (Int) => (Int) => Int = <function1>
scala> Function.uncurried(f)
res0: (Int, Int) => Int = <function2>
Extending retonym's answer, for completeness
val f : Int => Int => Int = a => b => a + b
val g: (Int, Int) => Int = Function.uncurried(f)
val h: ((Int, Int)) => Int = Function.tupled(g)
The converse functions for both of these operations are also provided on the Function object, so you could write the above backwards, if you wished
val h: ((Int, Int)) => Int = x =>(x._1 + x._2)
val g: (Int, Int) => Int = Function.untupled(h)
val f : Int => Int => Int = g.curried //Function.curried(g) would also work, but is deprecated. Wierd
Just to round out the answer, although there is a library method to do this, it may also be instructive to do it by hand:
scala> val f = (i: Int) => ((s: String) => i*s.length)
f: (Int) => (String) => Int = <function1>
scala> val g = (i: Int, s: String) => f(i)(s)
g: (Int, String) => Int = <function2>
Or in general,
def uncurry[A,B,C](f: A=>B=>C): (A,B)=>C = {
(a: A, b: B) => f(a)(b)
}
Similar to the answer by Rex Kerr but easier to read.
type A = String
type B = Int
type C = Boolean
val f: A => B => C = s => i => s.toInt+i > 10
val f1: (A, B) => C = f(_)(_)