Elegant ways to return multiple values from a function

≡放荡痞女 提交于 2019-12-18 01:34:12

问题


It seems like in most mainstream programming languages, returning multiple values from a function is an extremely awkward thing.

The typical solutions are to make either a struct or a plain old data class and return that, or to pass at least some of the parameters by reference or pointer instead of returning them.

Using references/pointers is pretty awkward because it relies on side effects and means you have yet another parameter to pass.

The class/struct solution is also IMHO pretty awkward because you then end up with a million little classes/structs that are only used to return values from functions, generating unnecessary clutter and verbosity.

Furthermore, a lot of times there's one return value that is always needed, and the rest are only used by the caller in certain circumstances. Neither of these solutions allow the caller to ignore unneeded return types.

The one language I'm aware of that handles multiple return values elegantly is Python. For those of you who are unfamiliar, it uses tuple unpacking:

a, b = foo(c)  # a and b are regular variables.
myTuple = foo(c)  # myTuple is a tuple of (a, b)

Does anyone have any other good solutions to this problem? Both idioms that work in existing mainstream languages besides Python and language-level solutions you've seen in non-mainstream languages are welcome.


回答1:


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);



回答2:


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




回答3:


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"



回答4:


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?




回答5:


Nobody seems to have mentioned Perl yet.

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



回答6:


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.




回答7:


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.




回答8:


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



回答9:


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.




回答10:


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



回答11:


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; 
}



回答12:


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




回答13:


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.




回答14:


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



来源:https://stackoverflow.com/questions/514038/elegant-ways-to-return-multiple-values-from-a-function

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