Scala: Определение функции правильного типа

#scala

Вопрос:

Я играл с кодом Scala и столкнулся с ошибкой компилятора, которую я не понимаю. Код генерирует вектор пар Int, а затем пытается его отфильтровать.

 val L = for (x lt;- (1 to 5)) yield (x, x * x)  val f = (x: Int, y: Int) =gt; x gt; 3 println(L.filter(f))  

Компилятор жалуется на попытку использовать f в качестве аргумента filter метод с сообщением об ошибке компилятора:

 error: type mismatch; found : (Int, Int) =gt; Boolean required: ((Int, Int)) =gt; Boolean  

Как правильно определить функцию f , чтобы она соответствовала требуемому типу функции? Я попытался добавить дополнительные круглые скобки (x: Int, y: Int) , но это дало:

 error: not a legal formal parameter  val f = ((x: Int, y: Int)) =gt; x gt; 3  ^  

Комментарии:

1. Эта проблема является аргументом в пользу устранения различия между списками аргументов и кортежами. То есть прямо сейчас вход-это (x,y) f(x,y) нечто иное, чем само голое (x,y) . К сожалению, технически нетривиально устранить это различие.

Ответ №1:

f имеет тип Function2[Int, Int, Boolean] . L тип является IndexedSeq[Tuple2[Int, Int]] и поэтому filter ожидает функции типа . Function1[Tuple2[Int, Int], Boolean] Каждая FunctionN[A, B, .., R] черта имеет метод tupled , который возвращает функцию типа Function1[TupleN[A, B, ..], R] . Вы можете использовать его здесь для преобразования f в тип, ожидаемый L.filter .

 println(L.filter(f.tupled)) gt; Vector((4,16), (5,25))  

В качестве альтернативы вы можете переопределить f значение a Function1[Tuple2[Int, Int], Boolean] следующим образом и использовать его напрямую.

 val f = (t: (Int, Int)) =gt; t._1 gt; 3 println(L.filter(f)) gt; Vector((4,16), (5,25))  

Ответ №2:

 val f = (xy: (Int, Int)) =gt; xy._1 gt; 3 println (L.filter (f))  

Если вы это сделаете

 val f = (x: Int, y: Int) =gt; x gt; 3  

вы определяете функцию, которая принимает два входа, что не то же самое, что функция, которая принимает пару входов в качестве параметра.

Сравнить:

 scalagt; val f = (x: Int, y: Int) =gt; x gt; 3 f: (Int, Int) =gt; Boolean = lt;function2gt;  scalagt; val f = (xy: (Int, Int)) =gt; xy._1 gt; 3 f: ((Int, Int)) =gt; Boolean = lt;function1gt;  

Ответ №3:

Если вы не хотите переписывать свою функцию на явно использующий Tuple2 (как предложено missingfaktor и неизвестным пользователем), вы можете определить неявный метод, чтобы сделать это автоматически. Это делает функцию f нетронутой (вы не обязаны всегда вызывать ее с параметром Tuple2) и более понятной, потому что вы все еще используете идентификаторы x и y.

 implicit def fun2ToTuple[A,B,Res](f:(A,B)=gt;Res):((A,B))=gt;Res =   (t:(A,B)) =gt; f(t._1, t._2) val L = for (x lt;- (1 to 5)) yield (x, x * x) val f = (x: Int, y: Int) =gt; x gt; 3 val g = (x: Int, y: Int) =gt; x % 2 gt; y % 3 L.filter(f) //gt; Vector((4,16), (5,25)) L.filter(g) //gt; Vector((3,9)) f(0,1) //gt; false f((4,2)) //gt; true  

Теперь каждая функция 2 также может использоваться как функция 1 с параметром Tuple2 в качестве параметра, поскольку для преобразования функции при необходимости используется неявный метод.

Для функций с более чем двумя параметрами неявное определение выглядит аналогично:

 implicit def fun3ToTuple[A,B,C,Res](f:(A,B,C)=gt;Res):((A,B,C))=gt;Res =   (t:(A,B,C)) =gt; f(t._1, t._2, t._3) implicit def fun4ToTuple[A,B,C,D,Res](f:(A,B,C,D)=gt;Res):((A,B,C,D))=gt;Res =   (t:(A,B,C,D)) =gt; f(t._1, t._2, t._3, t._4) ...