Crea un oggetto il cui stato cambia al momento dell'assegnazione


31

Trovo profondamente strano che ciò sia possibile in Ruby (non dirò immediatamente come):

obj = #code redacted

print obj.state # Some value.

LValue = obj

print obj.state # Different value!

La tua sfida è quella di creare il codice all'incirca di questo modulo. Crea un oggetto e assegnalo a una variabile. Dovrebbe avere un attributo definito (o metodo deterministico, idempotente) come statesopra, che cambia dopo che l'oggetto è stato assegnato a un nuovo identificatore ( LValuesopra), anche se si utilizza ancora il vecchio identificatore ( objsopra) per fare riferimento ad esso.

Modifica per enfasi : stateo l'equivalente deve essere idempotente, quindi la creazione di un accessorio che modifica il valore, o per qualsiasi altro motivo restituisce risultati diversi quando viene chiamato più volte di seguito, non è una soluzione valida. O, più semplicemente, deve essere il compito che cambia lo stato.

Qualsiasi lingua con assegnazione è idonea, anche se probabilmente ce ne sono alcune in cui non esiste una soluzione completamente legittima. Pubblicherò la mia risposta su Ruby se nessun altro lo riceverà dopo pochi giorni e accetterò le risposte più votate su base continuativa.


La LValue = objlinea deve essere obbligatoria per statecambiare effettivamente? (Potrei semplicemente creare una proprietà in C # che aumenta ogni volta che la ottieni)
Tim S.

2
Sì, è quello che intendevo dicendo che il metodo doveva essere idempotente. Modificherò per renderlo più chiaro.
histocrat

Ok grazie. Devo aver sorvolato quella parte.
Tim S.

4
Restituirebbe semplicemente il refcount dell'oggetto funzionante?
Nick T,

Alterazioni distruttive sull'oggetto stesso? EmacsLisp: (setq a (list "val")) (setq b (nconc a "val2"))ad esempio. afinisce per valutare come ("val" . "val2")a quel punto.
Jonathan Leech-Pepin,

Risposte:


30

C ++

Questo è banale usando gli strumenti giusti.

#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;
}

Produzione:

3 4
4 5 2 3

12
Nel momento in cui ho visto il titolo, ho saputo che qualcuno avrebbe sovraccaricato l'operatore. È il modo ovvio. Avere un voto.

17

PHP (build di debug,> = 5.4)

Usiamo refcount dell'oggetto in un getter. (Quindi, per incarico, il conteggio aumenta e le variazioni di valore)

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 #

Due semplici opzioni:

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();
}

Oppure potremmo semplicemente scrivere nella stessa memoria:

[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, molto più breve delle altre risposte qui

\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.

Come sistema di composizione, TeX ha un tipo "box", che contiene materiale composto. Poiché il caso d'uso più comune è spostare questo materiale, dividerlo, ecc., Anziché crearne copie, le scatole vengono normalmente eliminate quando utilizzate (o meglio, le variabili "scatola" sono puntatori e solo un puntatore alla volta può puntare in una casella reale in memoria). Non c'è bisogno di alcuna magia.


8

C ++ 11 (Quindi voi ragazzi avete dimenticato 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

Questo è in qualche modo simile alla risposta D di Hugo, ma è un po 'più nascosto (in parte perché chi # $% ^ conosce Object Oriented 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

L'output è

this%state = 1
this%state = 0

Se riesci a capire cosa è successo, punti bonus a te! Altrimenti:

Quando si chiama la procedura setNewnel modulo call b%setNew(a), bè implicitamente il primo argomento, non il secondo.


7

PowerShell

Questo crea un oggetto la cui stateproprietà sono i nomi delle variabili che puntano all'oggetto.

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

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

Produzione

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

Nota: questo non funziona se l'assegnazione avviene in un ambito figlio.

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

Uscite

Before: a,this
After: a,this

Get-Variable è intelligente!
mazzy,

5

Perl 5

Ecco un modo per farlo in 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};

Questo produce:

1
2

Spiegazione:

Questa è un'applicazione diretta del sovraccarico . In particolare, sovraccarico l'operatore di conversione delle stringhe "", che viene chiamato quando viene assegnato l'oggetto sovraccarico substr()(che, sì, è un valore legale in Perl).

Ci sono anche molte variabili speciali in Perl che restringono tutto ciò che è loro assegnato. Ad esempio, funziona anche quanto segue:

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

Soluzione alternativa

Ecco un altro modo per farlo:

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;

Ecco stateun metodo (potremmo renderlo un attributo con ulteriori shenanigans di legame / sovraccarico, ma ciò complicherebbe le cose) che conta letteralmente il numero di riferimenti all'oggetto. Pertanto, a differenza della prima soluzione, è necessario assegnare effettivamente $obja una variabile normale che può contenere un riferimento a oggetto per modificare lo stato.


5

JavaScript

Ok, quindi ho realizzato una versione più breve che funziona come SSCCE, ma non tenta più di analizzare correttamente JavaScript, quindi il conteggio dei riferimenti potrebbe non funzionare quando inserito in uno script più complesso.

(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
Ti va di spiegare cosa stai facendo?
Ryan,

5
... che cosa è questo mondo? O_o
Maniglia della porta

@Doorknob Un getter che restituisce il risultato, di chiamare una funzione, che conta la frequenza con cui un identificativo viene referenziato come rval, in un'espressione di assegnazione, all'interno di un dato sourcetext fino a una data riga, passando la sua sorgente di funzioni che racchiude e la riga il getter è stato chiamato come argomento. Tutto il resto è un tokenizer provvisorio disordinato. --- Non so come altrimenti dovrei chiamarlo . In altre parole. In altre parole: il getter conta il numero di assegnazioni di riferimenti a x fino alla linea da cui è stato chiamato, il resto è un tokenizer non finito.
C5H8NNaO4,

1
Il più lungo ... e il più largo!
Nicolas Barbulesco

1
@NicolasBarbulesco L'ho reso più breve
C5H8NNaO4

4

Pitone

Sta barando un po ', ma che ne dici di:

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

sebbene ciò possa essere esteso ad altre lingue che supportano distruttori impliciti / espliciti

#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;
}

L'operatore di assegnazione predefinito esegue una copia superficiale. Quindi l'oggetto ricevente possiede ancora il puntatore e qualsiasi modifica influisce implicitamente sull'oggetto originale;


1
Sì, a newsenza un singolo deletenel programma. Anche se, per questo compito, è abbastanza buono, penso :)
Ruslan,

Quali sono gli output?
Nicolas Barbulesco,

1
Da quello che ho capito (C ++ è molto lontano ...), qui il compito non cambia lo stato . Altrimenti, sposta la coutriga in alto prima di }e indica se funziona. :-)
Nicolas Barbulesco,

4

Scala

Le conversioni implicite ti consentono di ottenere questo risultato durante l'assegnazione a una normale variabile locale:

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)

Puoi farlo anche con tipi dedotti:

var s = ""
s = obj

Puoi anche usare un metodo setter personalizzato, sebbene ciò richieda che il valore L sia un campo:

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);
}

Produzione:

Obj(0)
Obj(1)

3

Rubino

Come promesso, ecco la risposta che ha ispirato la domanda.

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.newcrea una classe anonima. Richiamare to_suna classe anonima fornisce la rappresentazione di stringa predefinita degli oggetti, che sembra #<Class:0x007fe3b38ed958>. Tuttavia, una volta che la classe è stata assegnata a una costante, to_sdiventa quella costante. In Ruby, una costante è una variabile che inizia con una lettera maiuscola, quindiobj è un riferimento alla classe che le consente di rimanere anonimo.

Il mio codice si avvolge to_sin un statemetodo, quindi l'output diventa

Has not been assigned
Assigned to LValue

A differenza della maggior parte delle soluzioni qui, questo funziona solo una volta: l'assegnazione obja un'altra costante non cambierà la sua rappresentazione di stringa e né assegnerà un nuovo valore a LValue.


3

In Java

Pensavo fosse impossibile in Java. Ma…

Classe principale:

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
  }

}

Classe Cat:

public class Cat {

  static Cat bestCat;

  public Cat() {
    super();
  }

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

}

Sono stato ispirato da @tbodt.


1
So che non è code-golf, ma ti rendi conto che puoi semplicemente rimuovere il costruttore ed è sempre lo stesso, giusto?
David Conrad,

2
Questo non è "un oggetto il cui stato cambia durante l'assegnazione". Stai manipolando un valore globale e quindi stampando qualcosa basato su di esso. Non è diverso da Cat.x = 2e quindi dalla stampa Cat.x.
Chris Hayes,

@Chris - Lo stato dell'oggetto si basa su un "valore globale". Quindi lo stato dell'oggetto cambia al momento dell'assegnazione. La domanda afferma :-) che lo stato può essere un metodo deterministico, idempotente. Il mio metodo state () è un tale metodo.
Nicolas Barbulesco,

No, lo stato dell'oggetto cambia in questa specifica assegnazione . Se lo avessi fatto, Cat otherCat = tomlo stato non sarebbe cambiato affatto. Ho difficoltà a credere che questo soddisfi la lettera o lo spirito delle regole.
Chris Hayes,

@Chris - Ovviamente l'oggetto cambia in questo compito! La domanda richiede un oggetto il cui stato viene modificato dall'assegnazione. Non per un oggetto il cui stato viene modificato da qualsiasi assegnazione.
Nicolas Barbulesco,

3

C ++

Questo comportamento è in realtà specificato nello standard (ed è per questo che è stato deprecato).

#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';
}

Produzione

some address
0

Il processo che causa questo è lo stesso della risposta di Abhijit ma senza richiedere una std::movee la stessa della risposta di Marin ma usando una classe standard invece di definirla io stesso.

Modifica: sto aggiungendo qualche spiegazione. Nell'output, "some address" sarà effettivamente un valore esadecimale per l'indirizzo dell'intero allocato. std::auto_ptrrilascia il puntatore del negozio quando assegnato a un altro auto_ptre imposta il puntatore interno su 0. La chiamata get()recupera l'accesso al puntatore del negozio.


Ho il sospetto che "l'output" qui non sia l'output reale.
Nicolas Barbulesco,

Puoi spiegarci cosa dovrebbe fare? Soprattutto il metodo get()? Perché dovrebbe restituire 0 alla fine?
Nicolas Barbulesco,

@Nicholas yep. Questo output non è il vero output, ma un output più generale (inoltre non avevo accesso a un compilatore, quindi non avevo un esempio di un indirizzo valido al momento).
JKor

1
Hm, questo non riesce a compilare su gcc 4.8.
Michael Hampton,

1
Ho corretto gli errori di compilazione. Ci sono ancora avvisi se si sta compilando per c ++ 11 perché auto_ptrè obsoleto.
JKor

3

Pitone

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

2

Python 2.x

Non sono riuscito a trovare un modo corretto per farlo senza definire una classe aggiuntiva.

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

Giava

Tutte le altre soluzioni utilizzano la forma del loro linguaggio di sovraccarico dell'operatore. Java non ha un sovraccarico dell'operatore, quindi ho pensato di essere bloccato. Ma ho pensato a qualcosa.

Ecco la classe principale:

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());
    }
}

Ci sono alcune righe sospette, ma non farebbero nulla se la Thingclasse fosse del tutto normale. Non è:

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();
    }
}

Non è garantito che funzioni a causa dei thread, ma l'ho testato su JDK 1.8u5 e funziona lì.



@KyleKanos Mi sono sbarazzato di tutti i caratteri unicode> U + 00FF
tbodt

1

Lisp comune

Definisco lo stato come il numero di variabili speciali associate a un vettore. Quindi, l'assegnazione a una variabile speciale cambia lo stato.

(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*))

Produzione:

1 
2 
2 
3 

Funziona solo con assegnazioni a variabili speciali, non a variabili lessicali, né a slot all'interno di un oggetto.

Attenzione do-all-symbols guarda in tutti i pacchetti, quindi manca variabili che non hanno pacchetto. Potrebbe contare due volte i simboli esistenti in più di un pacchetto (quando un pacchetto ha importato il simbolo da un altro pacchetto).

Rubino

Ruby è quasi lo stesso, ma definisco stato il numero di costanti che si riferiscono a un array.

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

Produzione:

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

Questa è una generalizzazione della risposta dell'istocrate agli oggetti di Ruby che non sono classi o moduli. L'avviso viene visualizzato perché la costante Config carica automaticamente del codice che ha generato l'avviso.


0

C ++

Il risultato può differire in piattaforme diverse. Testato su 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;
}

Produzione:

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);
}

Produzione:

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

Cosa fa questo? Questo sta sovraccaricando l'operatore =?
Nicolas Barbulesco,

@Nicolas Non esattamente. È quando si esegue il casting da a Ba an A, perché implicit operator A(B b)ha effetti collaterali.
ClickRick
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.