Crie um objeto cujo estado seja alterado na atribuição

31

Acho profundamente estranho que isso seja possível em Ruby (não vou dizer imediatamente como):

obj = #code redacted

print obj.state # Some value.

LValue = obj

print obj.state # Different value!

Seu desafio é criar código aproximadamente neste formulário. Crie um objeto e atribua-o a uma variável. Ele deve ter algum atributo definido (ou método determinístico e idempotente) como stateacima, que muda após o objeto ser atribuído a um novo identificador ( LValueacima), mesmo se você ainda usar o identificador antigo ( objacima) para fazer referência a ele.

Edite para enfatizar : stateou o equivalente deve ser idempotente, portanto, criar um acessador que modifique o valor ou, por qualquer outro motivo, retorne resultados diferentes quando chamado várias vezes seguidas, não é uma solução válida. Ou, mais simplesmente, deve ser a atribuição que muda de estado.

Qualquer idioma com atribuição é elegível, embora haja provavelmente alguns em que não há solução totalmente legítima. Vou postar minha resposta em Ruby se ninguém mais receber depois de alguns dias e aceitar as respostas mais votadas em uma base contínua.

histocrata
fonte
LValue = objÉ necessário que a linha seja staterealmente alterada? (Eu poderia apenas fazer uma propriedade em C # que incrementos cada vez que você obtê-lo)
Tim S.
2
Sim, era isso que pretendia dizer que o método precisava ser idempotente. Vou editar para deixar isso mais claro.
histocrat
Ok obrigado. Eu devo ter encoberto essa parte.
Tim S.
4
Simplesmente retornar o refcount do objeto funcionaria?
Nick T
Alterações destrutivas no próprio objeto? EmacsLisp: (setq a (list "val")) (setq b (nconc a "val2"))por exemplo. aacaba avaliando ("val" . "val2")nesse momento.
Jonathan Sanguessuga-Pepin

Respostas:

30

C ++

Isso é trivial usando as ferramentas certas.

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

Saída:

3 4
4 5 2 3
marinus
fonte
12
No momento em que vi o título, sabia que alguém sobrecarregaria o operador. É o caminho óbvio. Tenha um voto positivo.
17

PHP (compilação de depuração,> = 5.4)

Usamos refcount do objeto em um getter. (Assim, pela atribuição, refcount aumenta e altera valores)

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);
bwoebi
fonte
14

C #

Duas opções simples:

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

Ou podemos simplesmente escrever na mesma memória:

[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(); }
}
NPSF3000
fonte
12

TeX, muito mais curto que as outras respostas aqui

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

Como sistema de composição tipográfica, o TeX possui um tipo de "caixa", que contém material tipográfico. Como o caso de uso mais comum é mover esse material, dividi-lo etc., em vez de fazer cópias dele, as caixas são normalmente excluídas quando usadas (ou melhor, as variáveis ​​"caixa" são ponteiros e apenas um ponteiro por vez pode apontar para uma caixa real na memória). Não há necessidade de mágica.

Bruno Le Floch
fonte
8

C ++ 11 (Então vocês se esqueceram de 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;
}
Abhijit
fonte
7

Fortran 03

Isso é um pouco semelhante à resposta D de Hugo, mas é um pouco mais oculto (em parte porque quem o # $% ^ conhece Fortran orientado a objetos)?

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

A saída é

this%state = 1
this%state = 0

Se você pode descobrir o que aconteceu, pontos de bônus para você! Se não:

Ao chamar o procedimento setNewno formulário call b%setNew(a), bé implicitamente o primeiro argumento, não o segundo.

Kyle Kanos
fonte
7

PowerShell

Isso cria um objeto cuja statepropriedade são os nomes das variáveis ​​que apontam para o objeto.

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

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

Saída

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

Nota: Isso não funciona se a atribuição ocorrer em um escopo filho.

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

Saídas

Before: a,this
After: a,this
Rynant
fonte
Get-Variable é inteligente!
Mazzy
5

Perl 5

Aqui está uma maneira de fazer isso no 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};

Isso gera:

1
2

Explicação:

Esta é uma aplicação direta de sobrecarga . Especificamente, sobrecarrego o operador de conversão de string "", que é chamado quando o objeto sobrecarregado é atribuído substr()(que, sim, é um valor legal em Perl).

Também existem muitas variáveis ​​especiais no Perl que restringem qualquer coisa que lhes seja atribuída. Por exemplo, o seguinte também funciona:

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

Solução alternativa

Aqui está outra maneira de fazer isso:

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;

Aqui stateestá um método (poderíamos torná-lo um atributo com mais travessuras de empate / sobrecarga, mas isso complicaria as coisas) que literalmente conta o número de referências ao objeto. Portanto, diferente da primeira solução, você realmente precisa atribuir $obja uma variável normal que pode conter uma referência de objeto para alterar o estado.

Ilmari Karonen
fonte
5

Javascript

Ok, então eu fiz uma versão mais curta que funciona como SSCCE, mas não tenta mais analisar o JavaScript corretamente, portanto a contagem de referência pode não funcionar quando inserida em um script mais complexo.

(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')
   }
}
C5H8NNaO4
fonte
2
Importa-se de explicar o que você está fazendo?
Ryan
5
... o que diabos é isso? O_o
Maçaneta da porta
@Doorknob Um getter que retorna o resultado, da chamada de uma função, que conta com que frequência um nome de identifi getter foi chamado como argumentos. Tudo o resto é um tokenizador provisório bagunçado. --- Eu não sei como mais devo chamá-lo . Em outras palavras. Em outras palavras: O getter conta o número de atribuições de referências para x até a linha da qual foi chamado, o restante é um tokenizador não concluído.
C5H8NNaO4 6/06/2014
1
O mais longo ... e o mais amplo!
Nicolas Barbulesco
1
@NicolasBarbulesco eu fiz isso mais curto
C5H8NNaO4
4

Python

Está enganando um pouco, mas que tal:

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
James_pic
fonte
4

C ++ 11

embora isso possa ser estendido para outros idiomas que suportam destruidores implícitos / explícitos

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

O operador de atribuição padrão executa uma cópia superficial. Portanto, o objeto receptor ainda possui o ponteiro e qualquer alteração afeta implicitamente o objeto original;

Abhijit
fonte
1
Sim, um newsem um deleteno programa. Embora, para esta tarefa é bom o suficiente eu acho :)
Ruslan
Quais são as saídas?
Nicolas Barbulesco
1
Pelo que entendi (C ++ está longe ...), aqui a atribuição não altera o estado . Caso contrário, mova a coutlinha para cima antes de }e diga se isso funciona. :-)
Nicolas Barbulesco
4

Scala

As conversões implícitas permitem fazer isso ao atribuir a uma variável local normal:

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)

Você também pode fazer isso com tipos inferidos:

var s = ""
s = obj

Você também pode usar um método setter personalizado, embora isso exija que o valor L seja um 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)
Dan Getz
fonte
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);
}

Saída:

Obj(0)
Obj(1)
Hugo Dubé
fonte
3

Rubi

Como prometido, aqui está a resposta que inspirou a pergunta.

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.newcria uma classe anônima. Chamar to_suma classe anônima fornece a representação padrão de string dos objetos, com a qual se parece #<Class:0x007fe3b38ed958>. No entanto, uma vez que a classe foi atribuída a uma constante, ela to_sse torna essa constante. No Ruby, uma constante é uma variável que começa com uma letra maiúscula, assim objcomo uma referência à classe que permite que ela permaneça anônima.

Meu código envolve to_sum statemétodo, então a saída se torna

Has not been assigned
Assigned to LValue

Diferentemente da maioria das soluções aqui, isso funciona apenas uma vez: atribuir obja outra constante não altera sua representação de string e também não atribui um novo valor a LValue.

histocrata
fonte
3

Em Java

Eu pensei que isso era impossível em Java. Mas…

Classe principal:

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

}

Fui inspirado por @tbodt.

Nicolas Barbulesco
fonte
1
Eu sei que não é código-golfe, mas você percebe que pode simplesmente remover o construtor e ainda é o mesmo, certo?
David Conrad
2
Este não é "um objeto cujo estado muda na atribuição". É você manipulando um valor global e imprimindo algo com base nele. Não é diferente da Cat.x = 2impressão Cat.x.
21430 Chris Hayes
@ Chris - O estado do objeto é baseado em um "valor global". Portanto, o estado do objeto muda na atribuição. A pergunta afirma ;-) que o estado pode ser um método determinístico e idempotente. Meu método state () é esse método.
Nicolas Barbulesco
Não, o estado do objeto muda nessa atribuição específica . Se eu fizesse Cat otherCat = tomo estado não teria mudado nada. É difícil acreditar que isso atenda à letra ou ao espírito das regras.
21430 Chris Hayes
@ Chris - É claro que o objeto muda nessa tarefa! A pergunta pede um objeto cujo estado seja alterado pela atribuição. Não para um objeto cujo estado seja alterado por qualquer atribuição.
Nicolas Barbulesco
3

C ++

Esse comportamento é realmente especificado no padrão (e é por isso que foi preterido).

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

Saída

some address
0

O processo que causa isso é o mesmo que a resposta de Abhijit, mas sem exigir std::movea mesma resposta de marinus, mas usando uma classe padrão em vez de defini-la eu mesmo.

Edit: eu estou adicionando alguma explicação. Na saída, "algum endereço" será realmente um valor hexadecimal para o endereço do número inteiro alocado. std::auto_ptrlibera o ponteiro de lojas quando atribuído a outro auto_ptre define seu ponteiro interno para 0. A chamada get()recupera o acesso ao ponteiro de lojas.

JKor
fonte
Eu suspeito que a "saída" aqui não é a saída real.
Nicolas Barbulesco
Você pode explicar o que isso deve fazer? Especialmente o método get()? Por que retornaria 0 no final?
Nicolas Barbulesco
@Nicholas sim. Esta saída não é a saída verdadeira, mas uma saída mais geral (eu também não tinha acesso a um compilador, portanto não tinha um exemplo de endereço válido no momento).
JKor
1
Hum, isso falha ao compilar no gcc 4.8.
Michael Hampton
1
Corrigi os erros de compilação. Ainda existem avisos se você estiver compilando para o c ++ 11 porque auto_ptrestá obsoleto.
JKor
3

Python

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

Python 2.x

Não consegui encontrar uma maneira adequada de fazer isso sem definir uma aula extra.

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()
seequ
fonte
2

Java

Todas as outras soluções usam a forma de sobrecarga do operador em seu idioma. Java não tem sobrecarga de operador, então eu pensei que estava preso. Mas eu vim com alguma coisa.

Aqui está a classe principal:

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

Existem algumas linhas suspeitas, mas elas não fariam nada se a Thingaula fosse completamente normal. Não é:

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

Não é garantido que funcione por causa dos threads, mas eu testei no JDK 1.8u5 e funciona lá.

tbodt
fonte
@KyleKanos se livrou de todos os caracteres Unicode> U + 00FF
tbodt
1

Lisp comum

Eu defino estado como o número de variáveis ​​especiais vinculadas a um vetor. Portanto, a atribuição a uma variável especial altera o estado.

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

Saída:

1 
2 
2 
3 

Ele funciona apenas com atribuições a variáveis ​​especiais, não a variáveis ​​lexicais nem a slots dentro de um objeto.

Cuidado com a do-all-symbolsaparência em todos os pacotes, para que ele perca as variáveis ​​que não têm pacote. Pode contar dois símbolos que existem em mais de um pacote (quando um pacote importou o símbolo de outro pacote).

Rubi

Ruby é quase o mesmo, mas eu defino estado como o número de constantes referentes a uma matriz.

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

Saída:

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

Esta é uma generalização da resposta do histocrata para objetos Ruby que não são classes ou módulos. O aviso aparece porque a constante Config carrega automaticamente algum código que o criou.

Kernigh
fonte
0

C ++

O resultado pode ser diferente em diferentes plataformas. Testado em 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;
}

Saída:

1234
13010
jingyu9575
fonte
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);
}

Saída:

a is 1, b is 2
a is 1, b is 2
a is -2, b is -2
a is -2, b is -2
ClickRick
fonte
O que isso faz? Isso está sobrecarregando o operador =?
Nicolas Barbulesco 07/07
@ Nicolas Não exatamente. É quando a transmissão de um Bpara um A, porque implicit operator A(B b)tem efeitos colaterais.
ClickRick