Создать объект, состояние которого изменяется при назначении


31

Я нахожу глубоко странным, что это возможно в Ruby (я не буду сразу говорить, как):

obj = #code redacted

print obj.state # Some value.

LValue = obj

print obj.state # Different value!

Ваша задача - создать код примерно такой формы. Создайте объект и назначьте его переменной. Он должен иметь некоторый определенный атрибут (или детерминированный, идемпотентный метод), как stateуказано выше, который изменяется после того, как объекту присвоен новый идентификатор (см. LValueВыше), даже если вы все еще используете старый идентификатор (см. objВыше) для ссылки на него.

Отредактируйте для выделения : stateили эквивалент должен быть идемпотентным, поэтому создание метода доступа, который изменяет значение или по любой другой причине возвращает разные результаты при вызове несколько раз подряд, не является допустимым решением. Или, проще говоря, это должно быть присвоение, которое изменяет состояние.

Любой язык с заданием является приемлемым, хотя, вероятно, есть такие, где нет полностью законного решения. Я опубликую свой ответ Ruby, если никто не получит его через несколько дней, и буду принимать ответы с наибольшим количеством голосов по мере поступления.


Должна ли LValue = objлиния быть обязательной для stateфактического изменения? (Я мог бы просто сделать свойство в C #, которое увеличивается каждый раз, когда вы его получаете)
Тим С.

2
Да, именно это я и имел в виду, говоря, что метод должен быть идемпотентным. Я отредактирую, чтобы сделать это более ясным.
гистократ

Хорошо спасибо. Должно быть, я затмил эту часть.
Тим С.

4
Будет ли просто возвращение refcount объекта работы?
Ник Т

Были бы деструктивные изменения самого объекта? EmacsLisp: (setq a (list "val")) (setq b (nconc a "val2"))например. aв конечном итоге оценки, как ("val" . "val2")на тот момент.
Джонатан Лич-Пепин

Ответы:


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
В тот момент, когда я увидел заголовок, я знал, что кто-то перегрузит оператора. Это очевидный способ. Имейте upvote.

17

PHP (отладочная сборка,> = 5.4)

Мы используем refcount объекта в геттере. (Таким образом, по присваиванию refcount увеличивается и значение изменяется)

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 имеет тип «box», который содержит материал набора. Поскольку наиболее распространенный вариант использования - это перемещать этот материал, разбивать его и т. Д., А не копировать его, ящики обычно удаляются при использовании (или, скорее, переменные «ящика» являются указателями, и только один указатель за раз может указывать фактической коробке в памяти). Не нужно никакой магии.


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

Фортран 03

Это несколько похоже на D-ответ Хьюго, но немного более скрыто (отчасти потому, что кто знает, что $ $ ^ знает объектно-ориентированный фортран)?

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

PowerShell

Это создает объект, чьим 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 - это умно!
Маззи

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()(который, да, является допустимым значением l в 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метод (мы могли бы сделать его атрибутом с дальнейшими махинациями связывания / перегрузки, но это усложнило бы ситуацию), который буквально подсчитывает количество ссылок на объект. Таким образом, в отличие от первого решения, вам фактически нужно назначить $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 Получатель, который возвращает результат вызова функции, который подсчитывает, как часто имя идентификатора ссылается как rval, в выражении присваивания, внутри заданного исходного текста вплоть до заданной строки, передавая его функции-источника и строку getter был вызван в качестве аргумента. Все остальное - грязный провизорный токенизатор. --- Я не знаю, как еще я должен это назвать . Другими словами. Другими словами: получатель подсчитывает количество назначений ссылок на x до строки, из которой он вызван, остальное - незавершенный токенизатор.
C5H8NNaO4

1
Самый длинный ... и самый широкий!
Николас Барбулеско

1
@NicolasBarbulesco Я сделал это короче
C5H8NNaO4

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

хотя это может быть расширено для других языков, которые поддерживают неявные / явные деструкторы

#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в программе. Хотя, для этой задачи это достаточно хорошо, я думаю :)
Руслан

Какие выходы?
Николас Барбулеско

1
Из того, что я понимаю (C ++ далеко ...), здесь назначение не меняет состояние . В противном случае переместите coutстроку вверх перед }и скажите, работает ли это. :-)
Николас Барбулеско

4

Scala

Неявные преобразования позволяют вам сделать это при назначении нормальной локальной переменной:

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

Вы также можете использовать пользовательский метод установки, хотя для этого требуется, чтобы 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. Но…

Основной класс:

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
  }

}

Класс Cat:

public class Cat {

  static Cat bestCat;

  public Cat() {
    super();
  }

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

}

Я был вдохновлен @tbodt.


1
Я знаю, что это не код-гольф, но вы понимаете, что можете просто удалить конструктор, и он все тот же, верно?
Дэвид Конрад

2
Это не «объект, состояние которого изменяется при назначении». Это вы манипулируете глобальным значением, а затем распечатываете что-то на его основе. Это ничем не отличается от Cat.x = 2печати Cat.x.
Крис Хейс

@Chris - состояние объекта основано на «глобальном значении». Таким образом, состояние объекта изменяется при назначении. Вопрос гласит ;-), что состояние может быть детерминированным, идемпотентным методом. Мой метод state () такой метод.
Николас Барбулеско

Нет, состояние объекта изменяется в этом конкретном назначении . Если бы я это сделал, Cat otherCat = tomсостояние бы совсем не изменилось. Мне трудно поверить, что это соответствует букве или духу правил.
Крис Хейс,

@Chris - Конечно объект изменяется в этом назначении! Вопрос требует объекта, состояние которого будет изменено присваиванием. Не для объекта, состояние которого может быть изменено любым назначением.
Николас Барбулеско

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

Процесс, который вызывает это, совпадает с ответом Абхиджита, но не требует std::move и такой же, как ответ маринуса, но использует стандартный класс вместо того, чтобы определять его сам.

Изменить: я добавляю некоторые объяснения. В выводе «некоторый адрес» фактически будет шестнадцатеричным значением для адреса выделенного целого числа. std::auto_ptrосвобождает свой указатель хранилищ при назначении другому auto_ptrи устанавливает свой внутренний указатель равным 0. Вызов get()возвращает доступ к указателю хранилищ.


Я подозреваю, что «вывод» здесь не является реальным выводом.
Николас Барбулеско

Можете ли вы объяснить, что это должно делать? Особенно метод get()? Зачем возвращать 0 в конце?
Николас Барбулеско

@ Николас да. Этот вывод не является истинным выводом, но является более общим выводом (у меня также не было доступа к компилятору, поэтому у меня не было примера действительного адреса в то время).
JKor

1
Хм, это не скомпилируется на gcc 4.8.
Майкл Хэмптон

1
Я исправил ошибки компиляции. Есть все еще предупреждения, если вы компилируете для c ++ 11, потому что auto_ptrустарела.
JKor


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 Избавился от всех символов
Юникода

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

Это обобщение ответа гистократа на объекты Ruby, которые не являются классами или модулями. Предупреждение появляется, потому что константа Config автоматически загружает некоторый код, который сделал предупреждение.


0

C ++

Результат может отличаться на разных платформах. Проверено на идеоне .

#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

Что это делает? Это перегрузка оператора =?
Николас Барбулеско

@ Николас Не совсем. Это когда происходит приведение Bк a A, потому что у implicit operator A(B b)него есть побочные эффекты.
ClickRick
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.