Mutabilidade e objetos; como gerenciar adequadamente os dados?

5

Objetos tendem a me confundir alguns. Para entender o conceito e o uso, certamente, mas sinto que o paradigma funcional restringe um pouco o meu uso deles.

No momento, estou criando um código simples para um jogo de cartas. Cada jogador pode ter um objeto simples, como este:

object player {
    name = "Steve";
    score = 0;
    hand = [/*...contains seven cards...*/];
}

e um objeto para um baralho:

object deck {
    cards = [/*...contains the other 45 cards...*/]
}

Então, para lidar com novas cartas, a maneira mais simples seria simplesmente transformar a carta this.hand e this.cards. Mas ... mutação.

Eu posso pensar em outras maneiras de fazer isso, mas as maneiras puramente funcionais que eu posso pensar em me sentir confuso.

Em que ponto você desenha a linha entre dados mutáveis e imutáveis, ou o que me falta?

    
por Ucenna 04.12.2016 / 21:47
fonte

3 respostas

1

É bem simples: se você quiser seguir o "caminho funcional puro", precisará de estruturas de dados puramente funcionais , que significa estruturas de dados imutáveis, que em linguagens OO significa objetos imutáveis.

Para representar baralhos de cartas, uma estrutura muito eficiente e imutável é uma pilha, implementada como uma única lista encadeada (e não como uma matriz!), talvez combinada com um contador de tamanho. Em seguida, as operações típicas de pilha, como adicionar uma placa ao topo, removendo uma da parte superior, podem ser implementadas como operações imutáveis em O (1). A iteração antecipada sobre todos os elementos pode ser implementada em O (N). O acesso aleatório a um determinado cartão se tornará uma operação O (N) (em vez de O (1) ao usar uma implementação de matriz), mas operações de cartão mais significativas podem ser implementadas sem o último, ou não tem um impacto sério no tempo de execução para os casos mais práticos.

the purely functional ways I can think of feel messy

Eu não me sinto assim - usar uma abstração de pilha não é necessariamente mais ou menos "confuso" do que usar uma abstração de array. O único obstáculo que vejo é que as bibliotecas padrão de linguagens OO de fluxo principal, como C ++, C # ou Java, não fornecem implementações padrão para algo como uma pilha imutável "out of the box". A maioria das linguagens funcionais provavelmente o fazem hoje. Apenas pesquise immutable stack <your favourite language> ou immutable object tutorial <language> e você provavelmente encontrará o que precisa.

No entanto, se você está mais acostumado com a modelagem OO clássica com estado mutável, você pode modelar e implementar seu jogo de cartas usando classes mutáveis (mas isso não funcionará muito mais).

    
por 05.12.2016 / 13:54
fonte
1

A mutação FP de uma entidade é modelada como uma criação de seu próximo estado. Primeiro, a entidade tem um estado representado por um objeto complexo arbitrário, no momento seguinte pode ter outro. Estes estados incluem estados para todas as propriedades da entidade, por exemplo, o estado do jogador pode incluir o estado da sua mão, o estado do mundo inclui o estado dos jogadores e o estado do deck.

Uma simples mutação, envolvendo apenas um aspecto do estado, é obtida por cópia superficial do estado anterior, substituindo um de seus valores de propriedade.

Linguagens funcionais (devem) ter uma sintaxe ou bibliotecas designadas para essa transformação.

Mutação simples sem usar bibliotecas designadas e na sintaxe JavaScript:

function score (world) {
   return {
     deck: world.deck,
     player: {
        name: world.player.name,
        score: world.player.score + 1,
        hand: world.player.hand
    }
}

Com aprimoramentos de sintaxe:

function score (world) {  world { player.score = world.player.score + 1 }; }

Exemplo hipotético com lógica não trivial, representando melhor as transições de estado real:

function win (world) {
  return {
     deck: world.deck.merge (world.player.hand),
     player: {name: world.player.name, score: world.player.score + 1, hand:[] }
}
    
por 05.12.2016 / 19:38
fonte
-3

OO e programação funcional são conceitos ortogonais. Então, se você misturá-los indevidamente, você não obterá o melhor de ambos, mas o pior de ambos.

A mutação de estruturas com funções é o pior que você pode fazer. Você tem efeitos colaterais e não sabe quem é o responsável pela consistência da estrutura.

Você encapsula a mutação em um objeto e deixa o objeto preservar a consistência OU usa estruturas de dados imutáveis e representa qualquer novo estado com uma nova estrutura de dados. Se você seguir isso, então você pode misturar os paradigmas sem inconvenientes.

    
por 05.12.2016 / 20:36
fonte