Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apply a list of parameters to a list of functions

Tags:

scala

I have a list of parameters like List(1,2,3,"abc","c") and a set of functions which validates the data present in the list like isNumberEven, isAValidString etc.

Currently, I take each value of the list and apply proper function which validates the data like isNumberEven(params(0)). This has led to big and messy code which is completely imperative in thinking.

I am expecting that it should be possible to do something like this in Scala -

List(1,2,3,"abc","c").zip(List(fuctions)).foreach{ x => x._2(x._1)}

However, this fails giving a runtime exception of type mismatch:

error: type mismatch; found : x._1.type (with underlying type Any) required: Int with String

I tried pattern matching on Function traits but it fails due to type erasure.

Any pointers will be appreciated as how can this be solved.

like image 782
mohit Avatar asked Aug 19 '14 14:08

mohit


People also ask

How do you pass multiple parameters to a function?

The * symbol is used to pass a variable number of arguments to a function. Typically, this syntax is used to avoid the code failing when we don't know how many arguments will be sent to the function.

What is the parameter list of a function?

The parameter list of a function describes the number and types of the arguments that the function accepts, and the number and types of the values it returns. The parameter list of a generic function is used to define the overall protocol of the generic function.

Is it possible to pass a list of data to a function as parameter?

You can send any data types of argument to a function (string, number, list, dictionary etc.), and it will be treated as the same data type inside the function.

Which parameters is used for a list in Python?

To create a list in Python, square brackets are used. The elements that are present inside these [] brackets are considered as elements of the list, each separated by a comma.


3 Answers

Very naive and non extensible implementation, I'm not very good with types, surely there's a better way:

val c = List(1,2,3,"abc","c")

def isEven(x: Int) = if(x % 2 == 0) true else false

def isUpperCase(x: String) = if(x.head.isUpper) true else false

c.map {
  case x: Int => isEven(x)
  case x: String => isUpperCase(x)
  case _ => false
}

You could also define list of functions:

scala>   val c = List(1,2,3,"abc","c")
c: List[Any] = List(1, 2, 3, abc, c)

scala>   def isEven(x: Int) = if(x % 2 == 0) true else false
isEven: (x: Int)Boolean

scala>   def isOdd(x: Int) = !isEven(x)
isOdd: (x: Int)Boolean

scala>   def isUpperCase(x: String) = if(x.head.isUpper) true else false
isUpperCase: (x: String)Boolean

scala>   def someString(x: String) = true
someString: (x: String)Boolean

scala>   val ints = List(isEven(_), isOdd(_))
ints: List[Int => Boolean] = List(<function1>, <function1>)

scala>   val strings = List(isUpperCase(_), someString(_))
strings: List[String => Boolean] = List(<function1>, <function1>)

scala>   c.map {
     |     case x: Int => ints.map(f => f(x)).exists(f => f(x))
     |     case x: String => strings.map(f => f(x)).forall(f => f(x))
     |     case _ => false
     |   }
res2: List[Boolean] = List(true, true, true, false, false)
like image 103
Ende Neu Avatar answered Nov 19 '22 21:11

Ende Neu


I just want to present a different approach without matching, although it is certainly sledgehammer-like.

First all functions are converted to functions of type Any => Boolean.

It iterates over the values in c. For each element it tries to find a function that is applicable and that results in true. If it doesn't find one, false is yielded.

def isEven(i: Int) = i % 2 == 0
def isGreaterThanTwo(i: Int) = i > 2
def hasB(s: String) = s.exists(_ == 'b')

def convert[T](func: T => Boolean) = (a: Any) => func(a.asInstanceOf[T])

val functions = List(isEven _, isGreaterThanTwo _, hasB _)
val c = List(1,2,3,"abc","c")

val result = {
  val convertedFunctions = functions.map(convert)

  c.map(elem => convertedFunctions.exists(func => Try(func(elem)) getOrElse false))
} 

with the result List(false, true, true, true, false).

The upside is that you can have as many functions as you like and it is therefore extensible. The downside is that you rely on exceptions. (which is usually not a good practice)

I first tried a solution with converting to PartialFunction and modifying the isDefined methods so it can be called on Any but then checks for a certain type. Then a lot of type-erasure happened and I couldn't make it work. Maybe that could be worth a shot.

If that is possible the code could be changed to:

def convert[T](func: T => Boolean) = new PartialFunction[Any, Boolean] {
  def isDefinedAt(x : Any) = ??? //check if x is an instance of T, type erasure problem
  def apply(x : Any) = func(x.asInstanceOf[T])
}

val result = {
  val convertedFunctions = functions.map(convert)

  c.map(elem => convertedFunctions.exists(func => 
  func.isDefinedAt(elem) && func(elem)))
}

which looks pretty nice.

like image 25
Kigyo Avatar answered Nov 19 '22 21:11

Kigyo


I'm not entirely certain on how you're planning on using the data afterwards, because 'foreach' would not actually return anything. But maybe this pattern-matched solution can help you achieve what you want?

scala> val f1 = (x:Int) => false
f1: Int => Boolean = <function1>

scala> val f2 = (x:String) => true
f2: String => Boolean = <function1>

scala> List(1,2,3,"abc","c").map {
    case x:String => f2(x)
    case x:Int => f1(x)
}
res3: List[Boolean] = List(false, false, false, true, true)
like image 31
fresskoma Avatar answered Nov 19 '22 21:11

fresskoma