【问题标题】:Languages that allow named tuples允许命名元组的语言
【发布时间】:2009-09-29 03:16:17
【问题描述】:

我想知道是否有任何语言允许命名元组。即:具有多个不同类型和可配置名称的变量的对象。

例如:

public NamedTuple<double:Speed, int:Distance> CalculateStuff(int arg1, int arg2)

var result = CalculateStuffTuple(1,2);

Console.WriteLine("Speed is: " + result.Speed.ToString())
Console.WriteLine("Distance is: " + result.Distance.ToString())

我可以想象动态如何支持这样的功能。我通常使用的静态语言(如 c#)可以做一个字典,但除非所有项目都属于同一类型,否则这不是类型安全的。或者您可以使用 Tuple 类型,但这意味着您有固定的成员名称(Var1、Var2 等)。

您也可以编写一个小的自定义类,但这是我想避免的情况。

我可以想象一种宏处理语言可以用静态语言为您编写类似的脚本,但我不知道这种语言。

这来自我对this question about return types的回答。

【问题讨论】:

  • 我想避免自定义类/结构的原因是我不喜欢打字!仅此而已。
  • 这一行应该不包括元组。 var 结果 = CalculateStuffTuple(1,2);

标签: c# return-value tuples


【解决方案1】:

老问题,但我认为需要更好的解决方案。

您可以利用 Tuple 类型获取命名参数,但将其包装在自定义命名类型中,该类型将 .Item1、.Item2 等包装在有意义的属性名称中。

我也讨厌元组具有使代码不可读的未命名参数这一事实,但不能忽略它节省的时间必须自己实现 IComparable、IStructuralEquatable 等,以便您可以安全地将结构用作字典键,例如。

我认为这是一个非常愉快的妥协:

public class Velocity : Tuple<double, double, string>
{
    public Velocity(double Speed, double Direction, string Units) : base(Speed, Direction, Units) { }
    public double Speed { get { return this.Item1; } }
    public double Direction { get { return this.Item2; } }
    public string Units { get { return this.Item3; } }
}

现在代替这些垃圾:

Tuple<double, double, string> myVelocity = new Tuple<double, double, string>(10, 2.34, "cm/s");
System.Diagnostics.Debug.Print("Speed: " + myVelocity.Item1);
System.Diagnostics.Debug.Print("Direction: " + myVelocity.Item2);
System.Diagnostics.Debug.Print("Units: " + myVelocity.Item3);

你可以这样做:

Velocity myVelocity2 = new Velocity(10, 2.34, "cm/s");
System.Diagnostics.Debug.Print("Speed: " + myVelocity2.Speed);
System.Diagnostics.Debug.Print("Direction: " + myVelocity2.Direction);
System.Diagnostics.Debug.Print("Units: " + myVelocity2.Units);

您仍然可以从所有美妙的元组功能中受益,这些功能让您可以将其用作字典等中的复杂键。

唯一的缺点是,如果您只打算在单个方法的范围内使用此元组,则必须在该方法的包含类的范围内声明一个类型。对于大多数应用程序,我认为这不是问题。

【讨论】:

  • 其实还不错。除了我必须有一堆小类围绕Tuple&lt;&gt;。但它比必须重新实现 IEquatable、IComparable 等要好。
  • 我不敢相信我从来没有想过这个。超级好用。
  • 正是我正在寻找的解决方案。太棒了:)
【解决方案2】:

在 C# 中,您有匿名类型;这些是相似的,但有其自身的局限性:

var result = new { Speed = 12.4, Distance = 8, Caption = "car 1" };

但是,很难将这些作为 调用者 使用,除非您使用“按示例转换”(脆弱)、反射或 dynamic。这三个中,最后一个是最开胃的。

dynamic result = GetSomething();
Console.WriteLine(result.Speed);
Console.WriteLine(result.Distance);

在大多数情况下,使用常规类会更好——但这种方法确实有一些实际用途;例如,看看它们如何在 ASP.NET MVC 中用于简单方便地传递配置信息(否则需要字典)。有点像 jQuery 允许您将选项作为属性传递给对象。

【讨论】:

  • 这似乎最接近我的要求(至少在 c# 中)。
【解决方案3】:

Eiffel 允许命名元组。

【讨论】:

    【解决方案4】:

    现在从 C# 7 开始支持此功能

    (double speed, int distance) CalculateStuff(int arg1, int arg2)
    
    var result = CalculateStuff(1,2);
    
    Console.WriteLine("Speed is: " + result.speed.ToString())
    Console.WriteLine("Distance is: " + result.distance.ToString())
    

    见:https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/

    【讨论】:

      【解决方案5】:

      我知道;

      • Python(动态、强类型)
      • 埃菲尔(静态,字符串类型)

      两者都可以在.net中使用

      也许还有 Lisp,你可以用 Lisp 做任何事情。

      【讨论】:

        【解决方案6】:

        您的意思是,类似于 Python 的 collections.namedtuple?好吧,Python(当前版本,2.6 和 3.1)支持它们;-)。但说真的,我不知道有一种静态类型语言将它们作为内置语言。

        【讨论】:

          【解决方案7】:

          我不确定这是否正是您要查找的内容,但在 Haskell 中,您可以拥有具有指定名称和类型的记录:

          data Movement = Movement { speed :: Double, distance :: Int } deriving (Show)
          
          main = do
              print $ Movement 3.14 100
              print Movement {distance = 5, speed = 2.1}
              print Movement {speed = 9, distance = -4}
          

          输出:

          移动{速度 = 3.14, 距离 = 100} 移动{速度 = 2.1, 距离 = 5} 移动 {速度 = 9.0, 距离 = -4}

          但从技术上讲,它不是一个元组。 Haskell 确实有元组,但据我所知,它们不能命名。

          这也与任何 C 派生语言中的简单结构相差无几。也许我错过了问题中的某些内容。

          【讨论】:

            【解决方案8】:

            在 C# 中使用结构或类有什么问题?

            public class SpeedDistance{
              public double Speed;
              public int Distance;
            }
            

            【讨论】:

            • 嗯,当然,公共领域是个坏主意...但是public double Speed {get;set;} 会这样做;-p
            • 我喜欢我的元组是不可变的。这需要只读变量和一个构造函数,它开始变得太长了。
            • 公共字段是好的,当且仅当这是所有类所包含的。但是元组也可以有很多其他不错的属性,在传递给其他库时会有所帮助。如果它们每次都是从头开始编写的,那么它们就不能互操作。
            【解决方案9】:

            存在几种这样的语言。这种命名元组的词是“记录”。 ML 语言家族有这样的记录,以及相应的类型。具体语言包括:SML、OCaml,以及重要的 F#。此链接解释了 F# 中的记录:http://en.wikibooks.org/wiki/F_Sharp_Programming/Tuples_and_Records#Defining_Records

            【讨论】:

              【解决方案10】:

              Swift 允许命名元组。你可以这样写:

              let interval = (start: 0, end: 10)
              let start = interval.start
              

              它们实际上是匿名结构。

              【讨论】:

                【解决方案11】:

                谢谢Alain。以下是我如何使用您的提示。

                用于轮播的动态图片加载器

                <div id="owl" class="owl-carousel owl-theme">
                    @foreach (var image in Model.Sketches)
                    {
                        <div class="item" >
                            <a href="@image.SketchHref" id="a_@image.SketchNumber" target="_blank" >
                                <img id="sketch_@image.SketchNumber" class="lazyOwl" style="border:1px solid #d1c7c7;outline : 0;max-height:350px;max-width:400px;" 
                                    title="click for full size" alt="@image.SketchName" data-src="@image.SketchHref" /></a>
                                    <div style="text-align:left;height:auto;vertical-align:bottom;padding:2px;font-size:1em;color:#DF3A01;">Sketch @image.SketchNumber of @Model.Sketches.Count()</div>
                        </div>
                    }
                    </div>
                

                对于 C#

                    public List<Sketches> Sketches 
                    {
                        get
                        {
                            List<Sketches> hrefs = new List<Sketches>();
                

                /* 图像名称与文件夹位置匹配 例如:1234101005_001.Gif 将等于“c:\images\1234\10\1005_BLD\” */

                            var sketchFolder = Regex.Replace(some_image, @"(\d{4})(\d{2})(\d{4})", @"c:\Sketches\$1\$2\$3\_BLD");
                            var sketchHref = Regex.Replace(some_image, @"(\d{4})(\d{2})(\d{4})", @"/sketches/$1/$2/$3/_BLD");
                            Int16 i = 0;
                
                            if (System.IO.Directory.Exists(sketchFolder))
                            {
                                List<string> gifs = GetGifs(sketchFolder);
                
                                gifs.ForEach(delegate(String gif)
                                {
                                    string s = sketchHref + "/" + gif;
                                    string f = sketchFolder + "/" + gif;
                
                                    if (System.IO.File.Exists(f))
                                    {
                                        Sketches sketch = new Sketches(s, (++i).ToString(), gif);
                                        hrefs.Add(sketch);
                                    }
                                    else // gif does not exist
                                    {
                                        Sketches sketch = new Sketches("placeholder.png", (++i).ToString(), gif);
                                        hrefs.Add(sketch);
                                    }
                                });
                            }
                            else // folder does not exist
                            {
                                Sketches sketch = new Sketches("placeholder.png", (++i).ToString(), "");
                                hrefs.Add(sketch);
                            }
                            return hrefs;
                        }
                    }
                
                public class Sketches : Tuple<string, string, string>
                {
                    public Sketches(string SketchHref, string SketchNumber, string SketchName) : base(SketchHref, SketchNumber, SketchName) { }
                    public string SketchHref { get { return this.Item1; } }
                    public string SketchNumber { get { return this.Item2; } }
                    public string SketchName { get { return this.Item3; } }
                }
                

                【讨论】:

                  【解决方案12】:

                  我不确定您需要它来做什么 - 元组只是一个包含不同类型数据的结构。如果你真的想要命名属性,你将不得不创建一个自定义类型或动态创建一个匿名类型。

                  我不知道有任何静态类型语言会支持这一点,但 C# 肯定不支持。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2012-02-21
                    • 1970-01-01
                    • 2018-03-20
                    • 2010-11-21
                    • 2013-09-10
                    • 1970-01-01
                    • 1970-01-01
                    • 2014-09-01
                    相关资源
                    最近更新 更多