Elegant ways to return multiple values from a function

会有一股神秘感。 提交于 2019-11-28 22:43:41

Pretty much all ML-influenced functional langues (which is most of them) also have great tuple support that makes this sort of thing trivial.

For C++ I like boost::tuple plus boost::tie (or std::tr1 if you have it)

typedef boost::tuple<double,double,double> XYZ;

XYZ foo();

double x,y,z;
boost::tie(x,y,z) = foo();

or a less contrived example

MyMultimap::iterator lower,upper;
boost::tie(lower,upper) = some_map.equal_range(key);

A few languages, notably Lisp and JavaScript, have a feature called destructuring assignment or destructuring bind. This is essentially tuple unpacking on steroids: rather than being limited to sequences like tuples, lists, or generators, you can unpack more complex object structures in an assignment statement. For more details, see here for the Lisp version or here for the (rather more readable) JavaScript version.

Other than that, I don't know of many language features for dealing with multiple return values generally. However, there are a few specific uses of multiple return values that can often be replaced by other language features. For example, if one of the values is an error code, it might be better replaced with an exception.

While creating new classes to hold multiple return values feels like clutter, the fact that you're returning those values together is often a sign that your code will be better overall once the class is created. In particular, other functions that deal with the same data can then move to the new class, which may make your code easier to follow. This isn't universally true, but it's worth considering. (Cpeterso's answer about data clumps expresses this in more detail).

PHP example:

function my_funct() {
    $x = "hello";
    $y = "world";
    return array($x, $y);
}

Then, when run:

list($x, $y) = my_funct();
echo $x.' '.$y; // "hello world"

If a function returns multiple values, that is a sign you might be witnessing the "Data Clump" code smell. Often data clumps are primitive values that nobody thinks to turn into an object, but interesting stuff happens as you begin to look for behavior to move into the new objects.

Writing tiny helper classes might be extra typing, but it provides clear names and strong type checking. Developers maintaining your code will appreciate it. And useful small classes often grow up to be used in other code.

Also, if a function returns multiple values, then it might be doing too much work. Could the function be refactored into two (or more) small functions?

Nobody seems to have mentioned Perl yet.

sub myfunc {
  return 1, 2;
}
my($val1, $val2) = myfunc();

As for Java, see Bruce Eckel's Thinking in Java for a nice solution (pp. 621 ff).

In essence, you can define a class equivalent to the following:

public class Pair<T,U> {
    public final T left;
    public final U right;
    public Pair (T t, U u) { left = t; right = u; }
}

You can then use this as the return type for a function, with appropriate type parameters:

public Pair<String,Integer> getAnswer() {
    return new Pair<String,Integer>("the universe", 42);
}

After invoking that function:

Pair<String,Integer> myPair = getAnswer();

you can refer to myPair.left and myPair.right for access to the constituent values.

There are other syntactical sugar options, but the above is the key point.

i thinks python's is the most natural way, when I had to do same thing in php the only was to wrap return into an array. it does have similar unpacking though.

Nosredna

Even if you ignore the wonderful newer destructuring assignment Moss Collum mentioned, JavaScript is pretty nice on returning multiple results.

function give7and5() {
  return {x:7,y:5};
}

a=give7and5();
console.log(a.x,a.y);

7  5

Both Lua and CLU (by Barbara Liskov's group at MIT) have multiple return values for all functions---it is always the default. I believe the designers of Lua were inspired by CLU. I like having multiple values be the default for calls and returns; I think it is a very elegant way of thinking about things. In Lua and CLU this mechanism is completely independent of tuples and/or records.

The portable assembly language C-- supports multiple return values for its native calling convention but not for the C calling convention. There the idea is to enable a compiler writer to return multiple values in individual hardware registers rather than having to put the values in a clump in memory and return a pointer to that clump (or as in C, have the caller pass a pointer to an empty clump).

Like any mechanism, multiple return values can be abused, but I don't think it is any more unreasonable to have a function return multiple values than it is to have a struct contain multiple values.

Often in php I'll use a referenced input:

public function Validates(&$errors=array()) {
   if($failstest) {
      $errors[] = "It failed";
      return false;
   } else {
      return true;
   }
}

That gives me the error messages I want without polluting the bool return, so I can still do:

if($this->Validates()) ...

In C++ you can pass a container into the function so the function can fill it:

void getValues(std::vector<int>* result){
  result->push_back(2);
  result->push_back(6);
  result->push_back(73);
}

You could also have the function return a smart pointer (use shared_ptr) to a vector:

boost::shared_ptr< std::vector<int> > getValues(){
  boost::shared_ptr< std::vector<int> > vec(new std::vector<int>(3));
  (*vec)[0] = 2;
  (*vec)[1] = 6;
  (*vec)[2] = 73;
  return vec; 
}

c#

public struct tStruct
{
    int x;
    int y;
    string text;
    public tStruct(int nx, int ny, string stext)
    {
         this.x = nx;
         this.y = ny;
         this.text = stext;
    }
}

public tStruct YourFunction()
{
    return new tStruct(50, 100, "hello world");
}

public void YourPrintFunction(string sMessage)
{
    Console.WriteLine(sMessage);
}

in Lua you call

MyVar = YourFunction();
YourPrintfFunction(MyVar.x);
YourPrintfFunction(MyVar.y);
YourPrintfFunction(MyVar.text);

Output: 50 100 hello world

Paul

By the way, Scala can return multiple values as follows (pasted from an interpreter session):

scala> def return2 = (1,2)
return2: (Int, Int)

scala> val (a1,a2) = return2
a1: Int = 1
a2: Int = 2

This is a special case of using pattern matching for assignment.

Here was my Ruby idea to return a special value that looks and acts like a scalar but actually has hidden attributes on it:

https://gist.github.com/2305169

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!