简体   繁体   English

从函数返回多个值的优雅方法

[英]Elegant ways to return multiple values from a function

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. 典型的解决方案是创建struct或普通旧数据并返回它,或者通过引用或指针传递至少一些参数而不是返回它们。

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. 我知道的一种优雅处理多个返回值的语言是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. 除了Python之外,现有主流语言中的两种习语以及您在非主流语言中看到的语言级解决方案都是受欢迎的。

Pretty much all ML-influenced functional langues (which is most of them) also have great tuple support that makes this sort of thing trivial. 几乎所有受ML影响的功能语言(大多数都是这样)也有很好的元组支持,这使得这种事情变得微不足道。

For C++ I like boost::tuple plus boost::tie (or std::tr1 if you have it) 对于C ++,我喜欢boost :: tuple plus boost :: tie(如果有的话,还是std :: tr1)

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. 一些语言,特别是Lisp和JavaScript,具有称为解构赋值或解构绑定的特性。 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 . 有关更多详细信息,请参阅此处获取Lisp版本此处获取(更具可读性)JavaScript版本

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). (Cpeterso关于数据块的答案更详细地表达了这一点)。

PHP example: PHP示例:

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. 似乎没有人提到过Perl。

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). 至于Java,请参阅Bruce Eckel 在Java中Thinking 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. 您可以参考myPair.leftmyPair.right来访问组成值。

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. 我认为python是最自然的方式,当我不得不在php中做同样的事情时,唯一的就是将返回包装成一个数组。 it does have similar unpacking though. 它确实有类似的拆包。

Even if you ignore the wonderful newer destructuring assignment Moss Collum mentioned , JavaScript is pretty nice on returning multiple results. 即使你忽略了Moss Collum提到的精彩的新的解构任务,JavaScript在返回多个结果时也相当不错。

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

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

7  5

Often in php I'll use a referenced input: 通常在php中我会使用引用的输入:

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: 这给了我想要的错误消息而不会污染bool返回,所以我仍然可以这样做:

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

Both Lua and CLU (by Barbara Liskov's group at MIT) have multiple return values for all functions---it is always the default. Lua和CLU(麻省理工学院的Barbara Liskov小组)都有多个所有函数的返回值 - 它始终是默认值。 I believe the designers of Lua were inspired by CLU. 我相信Lua的设计师受到了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. 在Lua和CLU中,这种机制完全独立于元组和/或记录。

The portable assembly language C-- supports multiple return values for its native calling convention but not for the C calling convention. 可移植汇编语言C--支持其本机调用约定的多个返回值,但不支持C调用约定。 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). 其目的是使编译器编写器能够在各个硬件寄存器中返回多个值,而不必将值放在内存中的clump中并返回指向该clump的指针(或者在C中,让调用者将指针传递给空丛()。

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. 像任何机制一样,可以滥用多个返回值,但我认为让函数返回多个值比使结构包含多个值更不合理。

Here was my Ruby idea to return a special value that looks and acts like a scalar but actually has hidden attributes on it: 这是我的Ruby想法,它返回一个特殊的值,它看起来像一个标量,但实际上有隐藏的属性:

https://gist.github.com/2305169 https://gist.github.com/2305169

c# 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 在Lua,你打电话

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

Output: 50 100 hello world 输出:50 100你好世界

Paul 保罗

By the way, Scala can return multiple values as follows (pasted from an interpreter session): 顺便说一句,Scala可以返回多个值,如下所示(从解释器会话粘贴):

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 . 这是使用模式匹配进行分配的特殊情况。

In C++ you can pass a container into the function so the function can fill it: C++您可以将容器传递给函数,以便函数可以填充它:

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 : 您还可以让函数将智能指针(使用shared_ptr )返回到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; 
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM