Inverse function in Scala

2020-02-28 19:30发布

问题:

Is there a way to express the inverse of any function in Scala?

For example if I have a function f like this:

(x: Int) => x + 1

I would like to be able write an inverse function g like:

(f(x): Int) => x // not a valid scala syntax

or

(x: Int) => inverse(f(x)) // inverse would return (x => x -1)

Do you know a way to do this kind of thing in Scala?

N.B: x => x+1 is just for the example. I'm looking for a generic way to solve this kind of task.

回答1:

No, something like that is not possible. The problem is that not all mathematical functions have inverses. From the Wikipedia entry on inverse functions:

Not all functions have an inverse. For this rule to be applicable, each element y ∈ Y must correspond to no more than one x ∈ X; a function ƒ with this property is called one-to-one, or information-preserving, or an injection.

For example, the square root (sqrt) function is the inverse of the square function (x^2) only when x >= 0, where the square root function is one-to-one. We can say that the negative of the square root function is the inverse of the square function when x < 0 only because x^2 = (-x)^2. But that is a special property of the square function and is certainly not true in general.



回答2:

Depending on your usage scenario you might be able to sort of do this by maintaining a map from (Function, Result) => Arguments, and then call a method such as inverse(f, r) which returns the arguments as stored in the map. However, this only works 1) if the original function is invoked before the inverse value is needed and 2) if the original function is injective (as ddk already pointed out).

There also is quite some implementation overhead involved (for example, you probably need a dedicated map for Function1 to Function22), especially if you want to reduce the amount of boilerplate code imposed on function implementers. Aspect-oriented programming could help here, annotations similar to EJB3's Interceptors might also work.

It looks, though, as if the usage scenario must be a pretty special one to justify all the hoops you have to jump through.



回答3:

You cannot express the inverse of a function, but you can express the inverse of its result and perhaps that would suit you.

This can be useful when you are passing the function around.

For example, if you have a function f: Int => Boolean that you're using as a parameter in a higher order function, you can wrap it in another function of the same type x => !f(x) that justs returns the inverse of the computed result:

def select(ls: List[String], p: String => Boolean): List[String] =
  ls.remove(x => !p(x))
// select: (ls: List[String], p: String => Boolean)List[String]

val li = List("one", "two", "three")
// li: List[java.lang.String] = List(one, two, three)

/* using select with some conditions */
select(li, _.length() > 3)  // equivalent to select(li, x => x.length() > 3)
// res0: List[String] = List(three)
select(li, _.length() <= 3) // equivalent to select(li, x => x.length() <= 3)
// res1: List[String] = List(one, two)

/* using remove with the same conditions */
li.remove(_.length() > 3)  // equivalent to li.remove(x => x.length() > 3)
// res2: List[java.lang.String] = List(one, two)
li.remove(_.length() <= 3)  // equivalent to li.remove(x => x.length() <= 3)
// res3: List[java.lang.String] = List(three)

NOTE: method remove in class List is deprecated and filterNotshould be used instead, but I think that in this example remove just reads better.



回答4:

From pragmatistic view, unapply method could be used to represent inverse functions:

object f {
  def apply(x: Int) = x + 1
  def unapply(x: Int) = Some(x - 1);
}

val x = 1
val f(y) = f(x)

assert(x == y)


回答5:

I am adding an answer because of another question marked as a duplicate of this one, and nobody seems to have mentioned this yet: it is theoretically possible to automatically compute the "inverse" (in the sense that it performs exhaustive search over the input, and answers "none" if there is no solution, within a finite amount of time) of functions over infinite sequences of binary digits: see the article "Seemingly impossible functional programs" (though it uses Haskell, not Scala).

Practical utility of this exhaustive search is another matter, of course.