#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) ...