创建一个对象,该对象的状态在分配时发生变化


31

我发现在Ruby中做到这一点非常奇怪(我不会马上说怎么做):

obj = #code redacted

print obj.state # Some value.

LValue = obj

print obj.state # Different value!

您的挑战是大致创建这种形式的代码。创建一个对象并将其分配给变量。它应该具有上面定义的某些已定义属性(或确定性,幂等方法),即使您仍使用旧标识符(上面)来引用它state,该对象在分配给新标识符(LValue上面)之后也会发生变化obj

编辑以强调state或等效项必须是幂等的,因此创建修改该值的访问器,或由于任何其他原因而导致在连续调用多次时返回不同的结果,不是有效的解决方案。或者,更简单地说,必须是更改状态的分配。

任何带有分配语言的语言都可以使用,尽管在某些语言中可能没有完全合法的解决方案。如果几天后没有其他人知道,我将发布我的Ruby答案,并滚动接受最高投票的答案。


必须更改该LValue = objstate才能实际更改吗?(我可以在C#中创建一个属性,每次获得它就会增加)
Tim

2
是的,这就是我要说的方法需要幂等的目的。我将进行编辑以使其更加清晰。
历史学家2014年

好,谢谢。我必须掩盖那部分。
蒂姆·S。

4
仅仅返回对象的引用计数即可吗?
尼克T

对物体本身的破坏性改变吗?EmacsLisp:(setq a (list "val")) (setq b (nconc a "val2"))例如。 a最终("val" . "val2")在那时进行评估。
Jonathan Leech-Pepin

Answers:


30

C ++

使用正确的工具,这是微不足道的。

#include <iostream>

using namespace std;

class Obj {
public:
   int state;

   Obj& operator= (Obj& foo) {
      foo.state++;
      this->state = foo.state - 2;
      return *this;
   }
};

int main() {
   Obj a, b, c, d;
   a.state = 3;
   b.state = 4;

   cout << a.state << " " << b.state << "\n";

   c = a;
   d = b;

   cout << a.state << " " << b.state << " " << c.state << " " << d.state << "\n";

   return 0;
}

输出:

3 4
4 5 2 3

12
看到标题的那一刻,我知道有人会做运算符重载。这是显而易见的方式。进行投票。

17

PHP(调试版本,> = 5.4)

我们在吸气剂中使用对象的引用计数。(因此,根据分配,引用计数增加且值更改)

class State {
    public function __get($arg) {
        ob_start();
        debug_zval_dump($this); // e.g. "object(State)#1 (0) refcount(6)"
        return ob_get_clean()[29];
    }
}

$obj = new State;
var_dump($obj->state);
$a = $obj;
var_dump($obj->state);

14

C#

两个简单的选择:

class Obj
{
    public int state;
    public static implicit operator int(Obj o)
    {
        return o.state++;
    }
}

static int LValueI;
static Obj LValueM { set { value.state++; } }
static void Main()
{
    var obj = new Obj { state = 1 };
    LValueI = obj;
    Console.WriteLine(obj.state); //2, caused by the implicit cast.

    LValueM = obj;
    Console.WriteLine(obj.state); //3, caused by the property setter.
    Console.ReadLine();
}

或者我们可以简单地写入相同的内存:

[StructLayoutAttribute(LayoutKind.Explicit)]
class Program
{
    [FieldOffset(0)]
    int state = 1;
    [FieldOffset(1)]
    int LValue;

    void Test()
    {
        var obj = this;

        Console.WriteLine(state);  //1
        LValue = state;
        Console.WriteLine(state);  //257
        Console.ReadLine();
    }
    static void Main() { new Program().Test(); }
}

12

TeX,比这里的其他答案要短得多

\setbox0=\hbox{Hello world!} % Put stuff in the box 0.
\message{\the\wd0}           % Print the width of the box => non-zero
\setbox2=\box0               % Put the box instead in box 2.
\message{\the\wd0}           % Now box 0 is void, hence has zero width.

作为排版系统,TeX具有“框”类型,其中包含排版材料。由于最常见的用例是移动该材料,对其进行分割等,而不是对其进行复制,因此通常在使用时删除框(或者,“框”变量是指针,一次只能指向一个指针)到内存中的实际盒子)。不需要任何魔术。


8

C ++ 11(所以你们已经忘记了unique_ptr / shared_ptr :-)

#include <iostream>
#include <memory>
using namespace std;
int main() {
    std::unique_ptr<int> u1(new int(0)), u2;
    std::shared_ptr<int> s1 = std::make_shared<int>(0), s2;
    std::cout<<u1.get()<<" "<<u2.get()<<" "<<std::endl;
    std::cout<<s1.use_count()<<" "<<s2.use_count()<<" "<<std::endl;
    u2 = std::move(u1);
    s2 = s1;
    std::cout<<u1.get()<<" "<<u2.get()<<" "<<std::endl;
    std::cout<<s1.use_count()<<" "<<s2.use_count()<<" "<<std::endl;
   return 0;
}

7

Fortran 03

这有点类似于Hugo的D答案,但隐藏得更多(部分是因为#$%^谁知道面向对象的Fortran)?

module objects
   implicit none

   type ObjDef
      integer :: state
    contains
      procedure :: initObject
      procedure :: printObject
      procedure :: setNew
   end type
 contains
   subroutine initObject(this)
     class(ObjDef) :: this
     this%state = this%state + 1
   end subroutine initObject

   subroutine printObject(this)
     class(ObjDef) :: this
     print '(a,i0)',"this%state = ",this%state
   end subroutine printObject

   subroutine setNew(this,that)
     class(ObjDef) :: this,that
     that%state = this%state
   end subroutine setNew

end module objects

program objectChange
   use objects
   type(ObjDef) :: a,b

   call initObject(a)
   call printObject(a)
   call b%setNew(a)
   call printObject(a)
end program objectChange

输出是

this%state = 1
this%state = 0

如果您能弄清楚发生了什么,奖金就可以给您!如果不:

setNew以形式调用过程时call b%setNew(a)b隐式是第一个参数,而不是第二个。


7

电源外壳

这将创建一个对象,其state属性是指向该对象的变量的名称。

$a = @{}| Add-Member -MemberType:16 -PassThru state -Value {
        (gv|?{$this -eq $_.Value}|%{$_.Name}) -join ','} 

'Before: ' + $a.state
$b = $a
'After: ' + $a.state

输出量

Before: a,this
After: a,b,this

注意:如果分配发生在子范围内,则此方法不起作用。

'Before: ' + $a.state
&{$b = $a}
'After: ' + $a.state

产出

Before: a,this
After: a,this

Get-Variable很聪明!
mazzy

5

Perl 5

这是在Perl中执行此操作的一种方法:

package Magic {
    sub new { bless {state => 1} }
    use overload '""' => sub { $_[0]{state}++ };
}
use feature 'say';

my $obj = new Magic;
say $obj->{state};
substr($_, 0) = $obj;
say $obj->{state};

输出:

1
2

说明:

这是重载的直接应用。具体来说,我重载了字符串转换运算符"",当将重载对象分配给它时会调用该运算符substr()(是的,这是Perl中的合法左值)。

Perl 中还有很多特殊变量,它们可以将分配给它们的所有内容都字符串化。例如,以下内容也适用:

my $obj = new Magic;
say $obj->{state};
$0 = $obj;
say $obj->{state};

替代解决方案

这是另一种方法:

package Magic {
    use Devel::Peek 'SvREFCNT';
    sub new { bless \my $foo }
    sub state { SvREFCNT ${$_[0]} }
}
use feature 'say';

my $obj = new Magic;
say $obj->state;
my $other = $obj;
say $obj->state;

在这里,state是一种方法(实际上可以使它成为具有更多tie /重载恶作剧的属性,但这会使事情复杂化),该方法从字面上计算对对象的引用次数。因此,与第一种解决方案不同,您实际上必须分配$obj一个普通变量,该变量可以保存对象引用以进行状态更改。


5

的JavaScript

好的,所以我制作了一个较短的版本,可以作为SSCCE使用,但是不再尝试正确地解析JavaScript,因此当放在更复杂的脚本中时,引用计数可能不起作用。

(function run () {
    var lineOne = getLine (1), a, b, x, y, z;
    var x = {
        get state () {
            var x=/([a-z]+)\s*=\s*([a-z]+)/,c;
            return 1 + Object.keys (c = run.toString ().split ('\n').slice (0,getLine (2)).filter (function (a) {return (x.test (a))}).reduce (function (a,b,c,d) {var r=b.match (x),t=r[2];while (a[t]){t=a[t]};a[r[1]]=t;return a}, {v:0})).reduce (function (a,b) {return (c[b]=="x"?1:0) + a},0)
        }
    };
    console.log (x.state);  //1
    console.log (x.state);  //1
    y = x;
    console.log (x.state);  //2
    z = y;
    console.log (x.state);  //3    
    a = z;
    b = a;
    console.log (x.state);  //5
    a = null;
    console.log (x.state);  //4
    b = null;
    console.log (x.state);  //3
})() //1 1 2 3 5 4 3 

function getLine(n) {
   try {
      to
   } catch (dat) {
      var stack = dat.stack.split('\n');
       for (var i = 0; i < stack.length; i++) {
           if (~stack[i].indexOf ('getLine')) break;          
       }
      return dat.stack.split ('\n')[i + ~~n].match (/:(\d+)/)[1] - ~~window.hasOwnProperty ('__commandLineAPI')
   }
}

2
照顾解释你在做什么?
瑞安

5
...这到底是什么?O_o
门把手

@Doorknob一个getter,它返回调用函数的结果,该函数计算赋值表达式中给定源文本中给定行中标识符名被引用为rval的频率,并传递其封闭函数source和getter被作为参数调用。其他所有内容都是临时的标记器。--- 我不知道该怎么称呼它。换一种说法。换句话说:getter计算对x的引用的分配数量,直到调用它的那一行为止,其余的是未完成的标记生成器。
C5H8NNaO4 2014年

1
最长的……最宽的!
Nicolas Barbulesco 2014年

1
@NicolasBarbulesco我把它缩短了
C5H8NNaO4 2014年

4

蟒蛇

有点作弊,但是如何:

import gc
class A(object):
    @property
    def state(self):
        return len(gc.get_referrers(self))

a = A()
print a.state
b = {"x": a}
print a.state
a.y = a
print a.state
del a
print b["x"].state

4

C ++ 11

尽管可以将其扩展为支持隐式/显式destrucors的其他语言

#include <iostream>
using namespace std;

class Foo {
    int *ptr;
public:
    Foo() {
        ptr = new int(0);
    }   
    int state() {
        return *ptr;
    }
    ~Foo() {
        (*ptr)++;
    }
};
int main() {
    Foo a, b;
    cout << a.state() << " " << b.state() << "\n";
    {
        Foo c, d;
        c = a;
        d = b;
    }
   cout << a.state() << " " << b.state()  << "\n";

   return 0;
}

默认赋值运算符执行浅表复制。因此,接收对象仍然拥有指针,并且任何更改都隐式影响原始对象。


1
是的,程序中new没有一个delete。虽然,对于我来说,这已经足够好了:)
Ruslan 2014年

输出是什么?
Nicolas Barbulesco 2014年

1
据我了解(C ++离我们很远……),这里的赋值不会改变state。否则,请在cout之前将线向上移动,}然后判断是否可行。:-)
Nicolas Barbulesco 2014年

4

斯卡拉

隐式转换使您可以在分配给普通局部变量的同时完成此操作:

import scala.language.implicitConversions

class Obj {
  var counter = 0
}

implicit def o2s(x: Obj): String = {
  x.counter += 1
  x.toString
}

val obj = new Obj
println(obj.counter)
val s: String = obj
println(obj.counter)

您也可以使用推断的类型来实现:

var s = ""
s = obj

您也可以使用自定义的setter方法,尽管该方法要求L值是一个字段:

object L {
  var _value = new Obj
  def value = _value
  def value_=(x: Obj): Unit = {
    _value = x
    x.counter += 1
  }
}

val obj = new Obj
println(obj.counter)
L.value = obj
println(obj.counter)

3

d

struct Obj {
    int state;

    void opAssign (ref Obj other) {
        ++other.state;
    }
}

void main () {
    import std.stdio;

    Obj obj, lvalue;
    writeln(obj);
    lvalue = obj;
    writeln(obj);
}

输出:

Obj(0)
Obj(1)

3

红宝石

如所承诺的,这是启发该问题的答案。

obj = Class.new { def self.state; to_s[/</] ? "Has not been assigned\n" : "Assigned to #{to_s}"; end }

print obj.state

LValue = obj

print obj.state

Class.new创建一个匿名类。调用to_s匿名类会提供对象的默认字符串表示形式,类似于#<Class:0x007fe3b38ed958>。但是,将类分配给常量后,to_s该常量就会变为常量。在Ruby中,常量是一个以大写字母开头的变量,因此obj引用该类也可以使其保持匿名状态。

我的代码包装to_s了一个state方法,因此输出变为

Has not been assigned
Assigned to LValue

与这里的大多数解决方案不同,此方法只能使用一次:分配obj给另一个常量不会更改其字符串表示形式,也不会给分配新值LValue


3

在Java中

我认为这在Java中是不可能的。但…

主班

public class MyAppOfCats {

  public static void main(String[] args) {
    Cat tom = new Cat();
    System.out.println(tom.state()); 
    // Output : NOT-BEST-CAT
    Cat.bestCat = tom;
    System.out.println(tom.state());
    // Output : BEST-CAT
  }

}

猫类:

public class Cat {

  static Cat bestCat;

  public Cat() {
    super();
  }

  public String state() {
      return ((this == Cat.bestCat) ? "BEST-CAT" : "NOT-BEST-CAT");
  }

}

@tbodt启发了我。


1
我知道这不是代码编写器,但是您意识到您可以删除构造函数,并且仍然保持不变,对吗?
David Conrad

2
这不是“状态随分配而改变的对象”。这是您操纵全局值,然后基于该值打印一些内容。它Cat.x = 2和然后打印没有什么不同Cat.x
克里斯·海斯

@Chris — 对象状态基于“全局值”。因此,对象状态随分配而改变。问题表明 ;-) 状态可以是确定性的幂等方法。我的方法state()就是这样的方法。
Nicolas Barbulesco 2014年

不,对象状态在此特定分配上更改。如果我做Cat otherCat = tom了,那状态根本就不会改变。我很难相信这符合规则的文字或精神。
克里斯·海斯

@Chris —当然,此任务会改变对象!该问题要求一个对象的状态可以通过赋值更改。不适用于其状态可以通过任何分配更改的对象。
Nicolas Barbulesco 2014年

3

C ++

该行为实际上是在标准中指定的(这就是为什么不推荐使用的原因)。

#include<iostream>
#include<memory>
int main()
{
    std::auto_ptr<int> a(new int(0));
    std::cout<<a.get()<<'\n';
    std::auto_ptr<int> b = a;
    std::cout<<a.get()<<'\n';
}

输出量

some address
0

导致此问题的过程与Abhijit的答案相同,但不需要std::move和marinus的答案相同,但使用标准类而不是自己定义它。

编辑:我添加一些解释。在输出中,“某些地址”实际上将是所分配整数地址的十六进制值。 std::auto_ptr分配给另一个对象时释放其存储指针auto_ptr,并将其内部指针设置为0。get()检索对商店指针的访问。


我怀疑这里的“输出”不是真实的输出。
Nicolas Barbulesco 2014年

您能解释一下这应该做什么吗?特别是方法get()?为什么最后会返回0?
Nicolas Barbulesco 2014年

@Nicholas是的。此输出不是真正的输出,而是更常规的输出(我也没有访问编译器的权限,所以当时我没有有效地址的示例)。
JKor

1
嗯,这无法在gcc 4.8上编译。
迈克尔·汉普顿

1
我修复了编译错误。如果auto_ptr不推荐使用c ++ 11进行编译,则仍然会有警告。
2014年

3

蟒蛇

import sys
class K:state = property(sys.getrefcount)

2

Python 2.x

如果不定义额外的类,我将找不到合适的方法。

class State(object):
    def __init__(self):
        self.state = 0
    def __set__(self, obj, other):
        # Keep different references
        other.state += 1
        self.state += 2

class Program(object):
    obj, value = State(), State() # Create two State-objects
    def __init__(self):
        print "Before assignment:", self.obj.state, self.value.state # 0 0
        self.value = self.obj # Set value to obj (supposedly)
        print "After  assignment:", self.obj.state, self.value.state # 1 2
        self.value = self.obj
        print "2nd    assignment:", self.obj.state, self.value.state # 2 4

Program()

2

爪哇

所有其他解决方案均使用其语言的运算符重载形式。Java没有运算符重载,所以我认为自己被困住了。但是我想出了一些办法。

这是主要的课程:

public class Program {
    public static void main(String[] args) {
        Thing thing = new Thing(0);
        System.out.println(thing.getState());
        Thing.otherThing = thing;
        Thread.sleep(1);
        System.out.println(thing.getState());
    }
}

有几行可疑内容,但是如果Thing课程完全正常,他们将不会做任何事情。不是:

public class Thing {
    private int state;

    public Thing(int state) {
        this.state = state;
    }

    public int getState() {
        return state;
    }

    // Please do your best to ignore the rest of this class.
    public static volatile Thing otherThing;
    static {
        Thread t = new Thread() {
            public void run() {
                Thing t = otherThing;
                while (true)
                    if (t != otherThing) {
                        t = otherThing;
                        t.state++;
                    }
            }
        };
        t.setDaemon(true);
        t.start();
    }
}

由于线程的原因,不能保证它能正常工作,但是我在JDK 1.8u5上对其进行了测试,并且在那里可以工作。



@KyleKanos摆脱了所有Unicode字符> U + 00FF
tbodt 2014年

1

普通口齿不清

我将状态定义为绑定到向量的特殊变量的数量。因此,分配给特殊变量会更改状态。

(defgeneric state (object)
  (:documentation "Get the state of this object."))

(defmethod state ((object vector))
  ;; The state of a vector is the number of symbols bound to it.
  (let ((count 0))
    ;; Iterate each SYM, return COUNT.
    (do-all-symbols (sym count)
      ;; When SYM is bound to this vector, increment COUNT.
      (when (and (boundp sym) (eq (symbol-value sym) object))
    (incf count)))))

(defparameter *a* #(this is a vector))
(defparameter *b* nil)
(defparameter *c* nil)

(print (state *a*))
(setf *b* *a*)
(print (state *a*))
(print (state *a*))
(setf *c* *a*)
(print (state *a*))

输出:

1 
2 
2 
3 

它仅适用于对特殊变量的赋值,不适用于词法变量,也不适用于对象内的插槽。

当心 do-all-symbols它会出现在所有程序包中,因此会丢失没有程序包的变量。它可能会重复计算多个软件包中存在的符号(当一个软件包从另一软件包中导入符号时)。

红宝石

Ruby几乎相同,但是我将状态定义为引用数组的常量的数量。

class Array
  # Get the state of this object.
  def state
    # The state of an array is the number of constants in modules
    # where the constants refer to this array.
    ObjectSpace.each_object(Module).inject(0) {|count, mod|
      count + mod.constants(false).count {|sym|
        begin
          mod.const_get(sym, false).equal?(self)
        rescue NameError
          false
        end
      }
    }
  end
end

A = %i[this is an array]
puts A.state
B = A
puts A.state
puts A.state
C = A
puts A.state

输出:

state-assign.rb:9:in `const_get': Use RbConfig instead of obsolete and deprecated Config.
1
2
2
3

这是histocrat对不是类或模块的Ruby对象的回答的概括。出现警告是因为Config常量自动加载了发出警告的某些代码。


0

C ++

在不同平台上,结果可能会有所不同。经ideone测试。

#include <iostream>
#include <cassert>
// File format: [ciiiiciiii...] a char (1 byte) followed by its state (4 bytes)
// Each group takes 5 bytes
char Buffer[30]; // 5*6, six groups

struct Group {
    char c;
    int state;
};

int main(void) {
    assert(sizeof(char) == 1);
    assert(sizeof(int) == 4);

    Group& first_group = *(Group*)(&Buffer[0]); // Group 1 is at 0
    Group& second_group = *(Group*)(&Buffer[5]); // Group 2 is at 5

    first_group.c = '2';
    first_group.state = 1234;

    std::cout << first_group.state << std::endl;

    second_group = first_group;

    std::cout << first_group.state << std::endl;

    return 0;
}

输出:

1234
13010

0

C#

class A
{
    public int N { get; set; }
    public override string ToString() { return N.ToString(); }
}
class B
{
    public int N { get; set; }
    public override string ToString() { return N.ToString(); }
    public static implicit operator A(B b) { b.N = -b.N; return new A { N = b.N }; }
}
public static void Test()
{
    A a = new A { N = 1 };
    B b = new B { N = 2 };
    Console.WriteLine("a is {0}, b is {1}", a, b);
    Console.WriteLine("a is {0}, b is {1}", a, b);
    a = b;
    Console.WriteLine("a is {0}, b is {1}", a, b);
    Console.WriteLine("a is {0}, b is {1}", a, b);
}

输出:

a is 1, b is 2
a is 1, b is 2
a is -2, b is -2
a is -2, b is -2

这是做什么的?这会使操作员超负荷=吗?
Nicolas Barbulesco 2014年

@Nicolas不完全是。从a转换B为an时A,因为implicit operator A(B b)存在副作用。
ClickRick 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.