C# 中的自动生存

发布于 2024-10-08 09:10:38 字数 412 浏览 0 评论 0原文

尝试着理解 Perl 的 Autovivification,根据它的听起来,它的工作原理似乎与 C# 中的动态类似,因为动态对象直到运行时才被分配类型,或者,我完全不在这吗?如果是这样,那么是否有一个类似的想法可以在 C# 中桥接,这是有意义的?

编辑
好吧,看来我还差得很远。那么,作为两部分问题的第二部分,C# 中是否存在概念上可比的内容?需要明确的是,我正在 C# 中寻找一个与 Autovivification 类似的概念。不必完全相同,但在概念上足够接近才能有意义。正如我之前所说,我绝不是一个 Perl 黑客或 Python 黑客,但是,我熟悉基于 C 的语言 C、C++、C#、java、javascript。我正在考虑 C# 的动态,但是,截至目前,我正在考虑基于此处信息的延迟加载(如果有帮助的话)。

Trying to wrap my head around perl's Autovivification and based on what it sounds like, It seems to work similar to dynamics in C# as a dynamic object is not assigned a type until runtime or, am I totally off here. If so then is there a comparable idea that I can bridge off of in C# that makes sense?

Edit
Okay so I'm apparently way off. So as second part of the 2 part question, is there anything conceptually comparable in C#? To be clear I'm looking for a concept in C# that is comparable to Autovivification. Doesn't have to be exactly the same but close enough conceptually to make sense. And as I stated eariler I am by no means a perl hacker or python hacker by any stretch of the imagination but, I am familar with c based languages C, C++, C#, java, javascript. I was thinking of C#'s dynamics but, as of right now I'm thinking lazy loading based on the info here if that helps....

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

信愁 2024-10-15 09:10:38

我无法用 C# 说话,但用外行人的话来说,Perl 的自动生存是在需要时立即从未定义的值创建容器对象的过程。

尽管大多数 Perl 相当动态,但 Perl 的取消引用语法在编译时明确指定了引用的类型。这使得解释器可以在定义变量之前知道它需要从变量中得到什么。

my $var;  # undefined

# to autovivify to an array:
@$var = 1..5;  # @ here implies ARRAY
$var[4] = 5;  # square brackets imply ARRAY
$#$var;        # $# implies ARRAY (returns the last index number)

# to autovivify to a hash:

%$var = (a => 1);   # % implies HASH
$var{asdf} = 5;    # curly braces imply HASH

这个列表可能会更长,但应该可以给您一个想法。

所以基本上,当你有这样的行时:

my $var;
$var->[1]{x}[3]{asdf}

Perl 查看 -> 的右侧并看到方括号。这意味着调用者 $var 必须是数组引用。由于调用者未定义,Perl 创建一个新数组并将其引用安装到 $var 中。然后,对于每个后续解除引用,都会重复相同的过程。

所以上面这行的真正意思是:

    (((($var //= [])->[1] //= {})->{x} //= [])->[3] //= {})->{asdf};

这是相当可怕的,因此是自动生存。 (//=是perl 5.10+中定义的或赋值运算符)

更新:

根据cjm的评论,将其放入一般非perl术语中,以实现自动生存在另一种语言中,您需要一个支持通过 [...]{...} 进行索引的惰性对象。当执行这些索引操作中的任何一个时,对象都会用数组或散列替换自身。每次访问该对象时,如果单元格为空,则应返回另一个惰性对象。

obj = new lazy_obj()

level1 = obj[4]   # sets obj to be an array, returns a new lazy_obj for level1

level2 = level1{asdf}  # sets level1 (and obj[4]) to a hash,
                       # returns a new lazy_obj for level2

所以基本上你需要两件事,创建一个支持数组和散列下标(或等效项)索引的对象的能力,以及一种机制,使对象可以在内存中用另一个对象替换自己(或者可以将自己锁定到一种解释,然后在内部存储新对象,

可以从以下伪代码开始:

class autoviv {
   private var content;

   method array_subscript (idx) {
       if (!content) {
           content = new Array();
       }
       if (typeof content == Array) {
            if (exists content[idx]) return content[idx];
            return content[idx] = new autoviv();
       } else {
            throw error
       }
   }

   method hash_subscript (idx) {
       if (!content) {
           content = new Hash();
       }
       if (typeof content == Hash) {
            if (exists content{idx}) return content{idx};
            return content{idx} = new autoviv();
       } else {
            throw error
       }
   }
   // overload all other access to return undefined, so that the value
   // still looks empty for code like:
   //
   // var auto = new autoviv(); 
   // if (typeof auto[4] == autoviv) {should run}
   // if (auto[4]) {should not run}
}

I can't speak to C#, but in layman's terms, Perl's autovivification is the process of creating a container object out of an undefined value as soon as it is needed.

Despite most of Perl being quite dynamic, Perl's dereferencing syntax unambiguously specifies the type of the reference at compile time. This allows the interpreter to know what it needs out of a variable before the variable is ever defined.

my $var;  # undefined

# to autovivify to an array:
@$var = 1..5;  # @ here implies ARRAY
$var[4] = 5;  # square brackets imply ARRAY
$#$var;        # $# implies ARRAY (returns the last index number)

# to autovivify to a hash:

%$var = (a => 1);   # % implies HASH
$var{asdf} = 5;    # curly braces imply HASH

This list could be longer, but should give you an idea.

So basically, when you have a line like this:

my $var;
$var->[1]{x}[3]{asdf}

Perl looks on the right side of the -> and sees square braces. This means that the invocant $var must be an array reference. Since the invocant is undefined, Perl creates a new array and installs its reference into $var. This same process is then repeated for every subsequent dereferencing.

So the line above really means:

    (((($var //= [])->[1] //= {})->{x} //= [])->[3] //= {})->{asdf};

which is fairly hideous, and hence autovivification. (//= is the defined-or assignment operator in perl 5.10+)

Update:

As per cjm's comment, to put this into general non-perl terms, to achieve autovivification in another language, you need a lazy object that supports indexing via [...] and {...}. When either of these indexing operations are performed, the object replaces itself with either an array or hash. Every time the object is then accessed, if the cell is empty, it should return another lazy object.

obj = new lazy_obj()

level1 = obj[4]   # sets obj to be an array, returns a new lazy_obj for level1

level2 = level1{asdf}  # sets level1 (and obj[4]) to a hash,
                       # returns a new lazy_obj for level2

So basically you need two things, the ability to create an object that supports indexing with both array and hash subscripts (or the equivalent), and a mechanism such that an object can replace itself in memory with another object (or that can lock itself to one interpretation, and then store the new object internally.

Something like the following pseudo-code could be a start:

class autoviv {
   private var content;

   method array_subscript (idx) {
       if (!content) {
           content = new Array();
       }
       if (typeof content == Array) {
            if (exists content[idx]) return content[idx];
            return content[idx] = new autoviv();
       } else {
            throw error
       }
   }

   method hash_subscript (idx) {
       if (!content) {
           content = new Hash();
       }
       if (typeof content == Hash) {
            if (exists content{idx}) return content{idx};
            return content{idx} = new autoviv();
       } else {
            throw error
       }
   }
   // overload all other access to return undefined, so that the value
   // still looks empty for code like:
   //
   // var auto = new autoviv(); 
   // if (typeof auto[4] == autoviv) {should run}
   // if (auto[4]) {should not run}
}
千柳 2024-10-15 09:10:38

Uri Guttman 的 自动生存教程 可能会有一些用处。

基本上,它是迄今为止未受影响的聚合体和聚合体成员在第一次使用时复活的能力。

例如,我可以这样做:

#!/usr/bin/perl

use strict; use warnings;
use Data::Dumper;

my @dummy;

push @{ $dummy[0] }, split ' ', 'this that and the other';
push @{ $dummy[1] }, { qw(a b c d) };

print Dumper \@dummy;

$dummy[0]$dummy[1] 在取消引用之前都不存在。

现在,如果您愿意放弃 strict (您不应该这样做),您还可以执行以下操作:

use Data::Dumper;

@$x = qw(a b c d);
print Dumper $x;

未定义的变量 $x 成为数组引用因为它正在被取消引用。

Uri Guttman's autovivification tutorial might be of some use.

Basically, it is the ability of hitherto untouched aggregates and members of aggregates to spring to life upon first use.

For example, I can do this:

#!/usr/bin/perl

use strict; use warnings;
use Data::Dumper;

my @dummy;

push @{ $dummy[0] }, split ' ', 'this that and the other';
push @{ $dummy[1] }, { qw(a b c d) };

print Dumper \@dummy;

Neither $dummy[0] nor $dummy[1] exist before they are dereferenced.

Now, if you are willing to forgo strict (which, you shouldn't be), you can also do things like:

use Data::Dumper;

@$x = qw(a b c d);
print Dumper $x;

whereby the undefined variable $x becomes an array reference because it is being dereferenced as such.

三寸金莲 2024-10-15 09:10:38

您可以通过创建一个 IDictionary 来实现类似自动验证的行为,它返回(并存储)一个新的 IDictionary (例如,递归地相同类型)当发生未设置键的 [] 时。这种方法在 Ruby 中的使用取得了巨大成功(一个示例)——然而,事实并非如此在静态类型语言中如此有用,因为没有办法干净地“获取”叶子值——至少在大多数现有合约(例如 IDictionary)的上下文中是这样。

随着 dynamic 的出现,这在 C# 中可能可以正常完成,但我不知道。

You can implement autovification-like behavior with creating say, an IDictionary<X,Y> that returns (and stores) a new IDictionary<X,Y> (e.g. recursively the same type) when a [] to an unset key occurs. This approach is used in Ruby to great success (an example) -- however, it's really not so useful in a statically typed language because there is no way to "get to" the leaf values cleanly -- at least in context of most existing contracts such as an IDictionary.

With the advent of dynamic, this may be possible in C# to do sanely, but I do not know.

篱下浅笙歌 2024-10-15 09:10:38

对于 C# 中字典的自动激活行为的简单实现,像这样的东西怎么样?显然这并不像 Perl 那样处理它,但我相信它具有相同的效果。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

//  The purpose of this class is to provide a dictionary with auto-vivification behaviour similar to Perl's
//  Using dict[index] will succeed regardless of whether index exists in the dictionary or not.
//  A default value can be set to be used as an initial value when the key doesn't exist in the dictionary

namespace XMLTest 
{
    class AutoDictionary<TKey,TValue> : Dictionary<TKey,TValue> {

        Object DefaultValue ;

        public AutoDictionary(Object DefaultValue) {
            this.DefaultValue = DefaultValue;
        }

        public AutoDictionary() {
            this.DefaultValue = null;
        }

        public new TValue this[TKey index] {
            get {
                try {
                    return base[index];
                }
                catch (KeyNotFoundException) {
                    base.Add(index, (TValue)DefaultValue);
                    return (TValue)DefaultValue ;
                }
            }

            set {
                try { 
                    base[index] = value ;
                }
                catch (KeyNotFoundException) {
                    base.Add(index, value);
                }
            }

        }
    }
}

How about something like this for a simple implementation of auto-vivification like behaviour of a Dictionary in C#? Obviously this doesn't handle it in the generic way that Perl does, but I believe that it has the same effect.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

//  The purpose of this class is to provide a dictionary with auto-vivification behaviour similar to Perl's
//  Using dict[index] will succeed regardless of whether index exists in the dictionary or not.
//  A default value can be set to be used as an initial value when the key doesn't exist in the dictionary

namespace XMLTest 
{
    class AutoDictionary<TKey,TValue> : Dictionary<TKey,TValue> {

        Object DefaultValue ;

        public AutoDictionary(Object DefaultValue) {
            this.DefaultValue = DefaultValue;
        }

        public AutoDictionary() {
            this.DefaultValue = null;
        }

        public new TValue this[TKey index] {
            get {
                try {
                    return base[index];
                }
                catch (KeyNotFoundException) {
                    base.Add(index, (TValue)DefaultValue);
                    return (TValue)DefaultValue ;
                }
            }

            set {
                try { 
                    base[index] = value ;
                }
                catch (KeyNotFoundException) {
                    base.Add(index, value);
                }
            }

        }
    }
}
£烟消云散 2024-10-15 09:10:38

我建议使用扩展方法而不是继承。

例如:

namespace DictionaryEx
{
    public static class Ex
    {
        public static TV Vivify<TK, TV>(this IDictionary<TK, TV> dict, TK key)
        {
            var value = default(TV);
            if (dict.TryGetValue(key, out value))
            {
                return value;
            }
            value = default(TV);
            dict[key] = value;
            return value;
        }

        public static TV Vivify<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue)
        {
            TV value;
            if (dict.TryGetValue(key, out value))
            {
                return value;
            }
            dict[key] = defaultValue;
            return defaultValue;
        }

        public static TV Vivify<TK, TV>(this IDictionary<TK, TV> dict, TK key, Func<TV> valueFactory)
        {
            TV value;
            if (dict.TryGetValue(key, out value))
            {
                return value;
            }
            value = valueFactory();
            dict[key] = value;
            return value;
        }
    }
}

I would recommend using extension methods instead of inheritance.

e.g.:

namespace DictionaryEx
{
    public static class Ex
    {
        public static TV Vivify<TK, TV>(this IDictionary<TK, TV> dict, TK key)
        {
            var value = default(TV);
            if (dict.TryGetValue(key, out value))
            {
                return value;
            }
            value = default(TV);
            dict[key] = value;
            return value;
        }

        public static TV Vivify<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue)
        {
            TV value;
            if (dict.TryGetValue(key, out value))
            {
                return value;
            }
            dict[key] = defaultValue;
            return defaultValue;
        }

        public static TV Vivify<TK, TV>(this IDictionary<TK, TV> dict, TK key, Func<TV> valueFactory)
        {
            TV value;
            if (dict.TryGetValue(key, out value))
            {
                return value;
            }
            value = valueFactory();
            dict[key] = value;
            return value;
        }
    }
}
花海 2024-10-15 09:10:38

使用索引器和 C# 4.0 动态,

class Tree
{
    private IDictionary<string, object> dict = new Dictionary<string, object>();
    public dynamic this[string key]
    {
        get { return dict.ContainsKey(key) ? dict[key] : dict[key] = new Tree(); }
        set { dict[key] = value; }
    }
}

// Test:
var t = new Tree();
t["first"]["second"]["third"] = "text";
Console.WriteLine(t["first"]["second"]["third"]);

DynamicObject 也可用于实现不同的语法,

using System;
using System.Collections.Generic;
using System.Dynamic;

class Tree : DynamicObject
{
    private IDictionary<object, object> dict = new Dictionary<object, object>();

    // for t.first.second.third syntax
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        var key = binder.Name;

        if (dict.ContainsKey(key))
            result = dict[key];
        else
            dict[key] = result = new Tree();

        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        dict[binder.Name] = value;
        return true;
    }

    // for t["first"]["second"]["third"] syntax
    public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
    {
        var key = indexes[0];

        if (dict.ContainsKey(key))
            result = dict[key];
        else
            dict[key] = result = new Tree();

        return true;
    }

    public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
    {
        dict[indexes[0]] = value;
        return true;
    }
}

// Test:
dynamic t = new Tree();
t.first.second.third = "text";
Console.WriteLine(t.first.second.third);

// or,
dynamic t = new Tree();
t["first"]["second"]["third"] = "text";
Console.WriteLine(t["first"]["second"]["third"]);

Using indexers and C# 4.0 dynamics,

class Tree
{
    private IDictionary<string, object> dict = new Dictionary<string, object>();
    public dynamic this[string key]
    {
        get { return dict.ContainsKey(key) ? dict[key] : dict[key] = new Tree(); }
        set { dict[key] = value; }
    }
}

// Test:
var t = new Tree();
t["first"]["second"]["third"] = "text";
Console.WriteLine(t["first"]["second"]["third"]);

DynamicObject can be used for implementing different syntaxes also,

using System;
using System.Collections.Generic;
using System.Dynamic;

class Tree : DynamicObject
{
    private IDictionary<object, object> dict = new Dictionary<object, object>();

    // for t.first.second.third syntax
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        var key = binder.Name;

        if (dict.ContainsKey(key))
            result = dict[key];
        else
            dict[key] = result = new Tree();

        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        dict[binder.Name] = value;
        return true;
    }

    // for t["first"]["second"]["third"] syntax
    public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
    {
        var key = indexes[0];

        if (dict.ContainsKey(key))
            result = dict[key];
        else
            dict[key] = result = new Tree();

        return true;
    }

    public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
    {
        dict[indexes[0]] = value;
        return true;
    }
}

// Test:
dynamic t = new Tree();
t.first.second.third = "text";
Console.WriteLine(t.first.second.third);

// or,
dynamic t = new Tree();
t["first"]["second"]["third"] = "text";
Console.WriteLine(t["first"]["second"]["third"]);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文