Crie um computador com um conjunto de instruções!

31

Aviso: estou disposto a dar uma recompensa a qualquer resposta que eu ache interessante.

Seu desafio é projetar um computador com um conjunto de instruções (OISC) completo :

Um OISC é uma máquina abstrata que usa apenas uma instrução - evitando a necessidade de um código de operação de linguagem de máquina. Com uma escolha criteriosa para a instrução única e com recursos infinitos, um OISC é capaz de ser um computador universal da mesma maneira que os computadores tradicionais que possuem várias instruções.

Aqui estão alguns exemplos de comandos únicos que formam um OISC completo para Turing.

Regras:

Você deve fornecer uma interpretação ou prova disso

Você deve fornecer um intérprete para o seu idioma. Esse intérprete deve ser restrito apenas pela memória / hora (por exemplo, não deve haver restrições impostas pelo usuário). Se você não fornecer um intérprete para o seu idioma (por qualquer motivo que não seja a preguiça), você deve provar que é possível que um seja escrito. Um intérprete deve ser possível .

Você deve provar sua integridade de Turing

Você deve incluir uma prova formal de que seu idioma está completo em Turing. Uma maneira simples de fazer isso é provar que ele pode interpretar ou ter o mesmo comportamento de outra linguagem completa de Turing. A linguagem mais básica para interpretar seria Brainf ** k .

Por exemplo, um idioma normal que possui todos os mesmos comandos que o Brainf ** k (e a mesma falta de restrições de memória impostas pelo usuário) está completo com Turing porque qualquer coisa que possa ser implementada no Brainf ** k pode ser implementada no idioma .

Aqui está uma lista de linguagens completas de Turing muito simples de implementar.

Requisitos adicionais do OISC

  • Esse OISC deve ter apenas uma instrução - não pode ter várias instruções, sendo que uma delas o torna completo em Turing.

  • Seu OISC pode usar qualquer sintaxe que você desejar. Você deve definir em sua resposta o que é instrução, o que são dados e o que é um não operacional (por exemplo, espaço em branco). Seja criativo!

  • Os argumentos não precisam ser apenas números inteiros. Por exemplo, /// é um belo exemplo de um OISC completo de Turing.

  • Como e se as entradas e saídas são obtidas e fornecidas são deixadas por você. A maioria dos OISCs implementa E / S através de locais específicos de memória, mas pode haver outras maneiras de fazê-lo, e você é incentivado a encontrar uma.

  • Uma resposta válida deve fornecer um código de exemplo no seu OISC, incluindo-o na postagem ou vinculando-o a um simples desafio resolvido no idioma.

Votação

Eleitores, lembre-se de não aprovar envios chatos. Exemplos:

  • Linguagem- equivalentes
  • Uma implementação de um OISC existente (respondedores, crie o seu próprio!)
  • Um "OISC" no qual o primeiro argumento especifica um comando para chamar ( exemplo )

No entanto, você deve votar em envios de criativos interessantes, como:

  • Um OISC baseado em uma equação matemática
  • Um ZISC completo de Turing baseado em uma rede neural
  • Um OISC no qual a E / S de saída ocorre de outras maneiras que não certos locais de memória

Ganhando

Como no , a resposta com mais votos ganha! Boa sorte!

MD XF
fonte
10
O que é uma "instrução"? E como os contamos?
Wheat Wizard
1
@NoOneIsHere Eu gostaria de saber o suficiente apenas para xD voto
Brian H.
2
Eu votei contra isso. Eu acho que essa é uma ideia muito interessante, mas você não explica exatamente o que é um OISC e como confirmar que algo é um. Eu fiz da BF um OISC, mas isso é claramente contrário ao espírito da questão, mas tecnicamente válido.
NoOneIsHere 2/02
1
@MDXF eu não acho que você começa ///: tem um comando de substituição, e tem comandos de impressão, que não é apenas um efeito colateral do comando de substituição
Destrutível Lemon
1
@NoOneIsHere Porque concurso de popularidade . Sim, é válido, mas tem uma pontuação baixa (número de votos), portanto não ganha.
user202729

Respostas:

20

XOISC

Este OISC é baseado no combinador X da Fokker, que é definido da seguinte forma:

X=λf .f (λg h x .g x (h x)) (λa b c .a)

Se reconhecermos o fato de que o cálculo do SKI é Turing completo, o -bin acima também é Turing completo. Isso ocorre porque S , K e eu podemos ser escritos em termos de X , assim:XSKIX

S=X (X X)K=X XI=S K K=X (X X) (X X) (X X)

Como o XOISC funciona

Internamente, o XOISC tem uma pilha (inicialmente vazia); a partir daí, a instrução que toma como argumento faz o seguinte:n

  • Pop elementos (funções f 1f N ) da pilha, pressione f 1 ( f 2 ( ( f N X ) ) )nf1fNf1 (f2 ((fN X)))

Quando não houver mais instruções, o XOISC enviará todos os argumentos da linha de comando (se houver) para a pilha, por exemplo:

[s1,, sMstack before, a1,, aNarguments]

O cálculo final será .((((s1 s2)) sM) a1))aN


Como a única instrução no XOISC usa apenas um argumento (deslocamento de memória), não há razão para usar um nome para essa instrução. Portanto, um arquivo de origem válido constituirá apenas números inteiros separados por novas linhas ou espaços em branco, como por exemplo:

0 0 2 0 1 0 1

Experimente online!

Exemplo

Vamos pegar o exemplo acima (pilha crescendo para a direita):

0pop 0 and apply (ie. push single X):[X]0again simply push X:[X, X]2pop 2 (a,b) and push a (b X):[X (X X)]0simply push X:[X (X X), X]1pop 1 (a) and push a X:[X (X X), X X]0simply push X:[X (X X), X X, X]1pop 1 (a) and push a X:[X (X X), X X, X X]

Por fim, avalie a pilha: ou com menos parênteses X ( X X ) ( X X ) ( X X ) que reconhecemos como a boa e velha identidade S K K função.((X (X X)) (X X)) (X X)X (X X) (X X) (X X)S K K

Conclusão de Turing

Ideia de prova

Para que o XOISC seja Turing completo, precisamos ser capazes de traduzir qualquer intercalação (válida) de parênteses e combinadores Isso é possível porque, quando popping, aplicando e pressionando, o faz de maneira associativa à direita (o aplicativo de função é associativo à esquerda).X

Para converter qualquer expressão existe uma maneira fácil de fazê-lo: Sempre apareça tantos elementos que, desde o início do nível atual de parênteses, restará apenas um elemento.X

Como exemplo, a expressão usada anteriormente: ((X (X X)) (X X)) (X X)

  • para obter o , simplesmente precisamos de umX0
  • Em seguida, estamos em um novo nível de parênteses, então, novamente, precisamos apenas de um 0
  • agora dois parênteses se fecham, então precisamos exibir 2 elementos: 2
  • novamente, estamos em um novo nível de parênteses, por isso precisamos de um 0
  • dois parênteses, feche novamente 2
  • e o mesmo novamente

Então, acabamos com um programa XOISC diferente (mas semântica equivalente):

0 0 2 0 2 0 2 Experimente online!

Se permanecermos nessa estratégia, podemos facilmente transformar qualquer expressão que consiste em combinadores em um programa XOISC que deixa apenas uma única função na pilha.X

Prova formal

Dado que o cálculo do SKI é Turing completo, precisamos mostrar duas coisas:

  1. o combinador é uma base para o cálculo SKIX
  2. XOISC é capaz de representar qualquer expressão formada com o combinador X

A primeira parte - provar as três igualdades na introdução - é muito entediante e consome espaço, também não é muito interessante. Então, em vez de colocá-lo neste post, você pode encontrar aqui * .

X

XXf gfg

0XF1FNG1GKfgf g

F1FN G1GK1 (GK+1)fggff g

Intérprete

Entradas

Como o cálculo lambda não digitado exige que definamos nossos próprios tipos de dados para tudo o que queremos e isso é complicado, o intérprete está ciente dos números da Igreja - isso significa que, quando você fornece entradas, ele automaticamente transforma os números no número correspondente da Igreja.

Como exemplo, aqui está um programa que multiplica dois números: Experimente online!

Você também pode fornecer funções como argumentos usando índices De Bruijn , por exemplo, o Scombinador \\\(3 1 (2 1))(ou λλλ(3 1 (2 1))). No entanto, também reconhece a S, K, Ie, claro, Xcombinator.

Saída

Por padrão, o intérprete verifica se a saída codifica um número inteiro; caso isso aconteça, ele gera o número correspondente (além do resultado). Por conveniência, há o -bsinalizador que diz ao intérprete para tentar combinar um booleano (veja o último exemplo).

Montador

É claro que qualquer linguagem de baixo nível precisa de um assembler que converta uma linguagem de alto nível para ela; você pode simplesmente usar qualquer entrada (veja acima) e traduzi-la para um programa XOISC usando a -abandeira, tente on-line! **


* Caso o link esteja inoperante, há uma cópia como comentário em HTML nesta postagem.

** Isso resulta em um programa que testa primalidade, experimente online!

ბიმო
fonte
1
Existe uma razão para você escolher o combinador X em vez do combinador Iota?
Esolanging Fruit
1
@EsolangingFruit: Sim, também existem várias outras opções. No final, eu optei por essa, porque ela usa menos aplicativos para criar o SK. Parecia que teria o melhor desempenho (tbh eu mesmo não fiz uma comparação).
ბიმო
1
Btw. há uma boa comparação de vários combinadores no artigo vinculado, se você estiver interessado.
ბიმო
19

Desenhar

Draw é um OISC atuando em uma grade 2D, marcando quadrados de maneira semelhante à máquina B de Wang. No entanto, para manter o idioma o mais simples e OISC-y possível, todas as instruções (das quais há um total de um) marcam o quadrado que acabou de pisar e, para poder parar, pisando em um quadrado marcado finaliza o programa.

O programa consiste em uma sequência de linhas contendo um identificador de linha (sequência arbitrária que não inclui # ou espaço em branco), dois números inteiros ( xe y) e mais dois identificadores de linha ( ae b).

O programa é executado como se segue:
Começando na linha identificada como startcom o ponteiro que aponta para a posição (0, 0), mover o indicador, o valor dado por xe ye marcar o quadrado do ponteiro está agora em (a menos que o quadrado já está marcado, nesse caso, a execução termina). Em seguida, pule para a linha ase pelo menos um dos quadrados diretamente adjacentes também estiver marcado e, bcaso contrário , para a linha .

Os intérpretes são incentivados a exibir o resultado final da grade como algum tipo de imagem, tela, etc.

Completude de Turing

Draw é Turing-complete, pois é possível compilar uma versão modificada (chamada Alternate) de uma máquina Minsky no idioma.

O alternativo age de maneira semelhante a uma máquina Minsky de dois contadores, mas há uma grande restrição nos comandos: os comandos devem alternar entre direcionar o primeiro e o segundo contadores. Para contornar esta modificação, foi adicionado um comando adicional: nop. Este comando não altera o contador de destino, o que possibilita "alterar" alterações consecutivas em um contador, satisfazendo a restrição descrita acima. Isso também significa que o registro a ser modificado não precisa ser fornecido e, para qualquer instrução, pode ser deduzido diretamente das instruções das quais a execução pode pular para ele.

Exemplo: esta máquina Minsky

1 inc A 2
2 inc A 3
3 dec A 3 4
4 halt

se transforma neste programa alternativo:

1 inc 2
2 nop 3
3 inc 4
4 nop 5
5 dec 6 8
6 nop 5
7 halt
8 halt

Essa restrição é necessária devido à maneira como o eventual programa Draw lida com os registros, ou seja, não faz nenhuma diferença entre eles. Em vez disso, o programa Draw simplesmente copia o registro que não foi alterado pela instrução anterior, modificando-o de acordo com a instrução que está sendo executada.

Em seguida, o programa alternativo é convertido diretamente no Draw da seguinte maneira:

O programa começa com este bloco.

start 0 0 a a
a 3 0 b b
b -3 1 c c
c 3 0 d d
d -3 2 e e
e 3 0 f f
f 3 -3 i1_a i1_a

inc, decE nopsão convertidos em quase da mesma forma como cada outra. Em todos os casos, não há diferença entre alterar o primeiro ou o segundo registrador (como explicado acima). Aqui está um incremento, equivalente a inc 2:

i1_y 0 -2 i1_z i1_y
i1_z 3 -1 i1_a i1_a
i1_a -5 1 i1_b i1_b
i1_b 0 2 i1_c i1_c
i1_c 0 2 i1_d i1_e
i1_d 0 2 i1_d i1_f

i1_e 5 0 i2_z i2_y
i1_f 5 0 i2_z i2_y

Altere os números nas i1_xpartes para o índice da instrução atual e nas i2_xpartes para o índice da próxima instrução a ser executada.

A nopinstrução pode ser traduzida como tal:

i1_y 0 -2 i1_z i1_y
i1_z 3 -1 i1_a i1_a
i1_a -5 1 i1_b i1_b
i1_b 0 2 i1_c i1_c
i1_c 0 2 i1_d i1_e
i1_d 0 2 i1_d i1_f

i1_e 5 -2 i2_z i2_y
i1_f 5 -2 i2_z i2_y

Este é um decréscimo:

i1_y 0 -2 i1_z i1_y
i1_z 3 -1 i1_a i1_a
i1_a -5 1 i1_b i1_b
i1_b 0 2 i1_c i1_c
i1_c 0 2 i1_d i1_e
i1_d 0 2 i1_d i1_f

i1_e 5 -2 i3_z i3_y
i1_f 5 -4 i2_z i2_y

i3_x refere-se à instrução a ser chamada se o contador já for 1.

Parar:

i1_y 0 0 0 0
i1_z 0 0 0 0

Troque os rótulos de maneira apropriada e simplesmente encadeie tudo. Fazer isso no exemplo acima fornece o programa Draw no repositório acima.

Intérpretes

Atualmente, existem dois intérpretes, ambos escritos em Python. Eles podem ser encontrados no repositório GitHub do Draw .

  1. draw.py : esse intérprete é destinado à linha de comando e usa a fonte do programa como argumento. Após cada etapa, ele exibe o comando que foi executado e a localização do ponteiro da instrução; depois que o programa pára, ele imprime o número de células marcadas.
  2. draw_golly.py : Esta versão usa o Golly para uma saída gráfica mais fácil, com o objetivo errado , levando a fonte através de uma caixa pop-up ao iniciar o script. O Golly pode ser um pouco exigente com o Python, portanto, certifique-se de ter o Python 2 instalado (e não misture o Golly de 32 bits com o Python de 64 bits ou vice-versa). A saída é fornecida pela grade de células incorporada da Golly.

A imagem a seguir é um exemplo de saída do segundo intérprete. A execução do programa de exemplo no repositório fornece isto (ou similar):

ivzem
fonte
1
Surpreendente! Parabéns por encontrar uma maneira muito única de fazer o desafio.
MD XF
Seu idioma não precisa parar para ficar completo. A regra 110 não termina, mas está completa mesmo assim.
Akangka
+1 para Golly, o melhor simulador de autômato celular de todos os tempos.
HighlyRadioactive
14

-3

Aqui está a essência.

Memória

A memória é um mapa de fitas, onde as teclas são cadeias de caracteres e os valores são números inteiros de tamanho arbitrário.

Além disso, há um conjunto de rótulos nos quais o programa pode ir.

Há uma pilha que contém os operandos, que são cadeias de caracteres.

Há uma violação, que controla onde as fitas da memória podem ser acessadas.

A única instrução

-. Primeiro, ele LABELtira uma string da pilha. Se isso não LABELestiver definido como rótulo, ele define o rótulo e limpa a fonte desse rótulo (ou seja, de onde foi enviado) e a instrução atual. Caso contrário, ele executa o seguinte cálculo, usando os dois principais valores Ae B:

if mem[A] < mem[B]:
    jump to LABEL
if mem[A] != mem[B]:
    mem[A]--
else:
    mem[B]++

Observe que, se houver argumentos em excesso ou insuficientes, o programa apresentará erros, mostrando o estado do programa.

O deslocamento pode ser modificado acessando o valor de ..

Código de exemplo

X-

i i X-
i i X-
i i X-
i i X-
i i X-
i i X-
i i X-

Isso define a variável icomo 7, incrementando os 7tempos.

X-

i i X-
i i X-
i i X-
LOOP-
    a a X-
    a a X-
    j i LOOP-

Isso multiplica i+1pela constante 2.

Prova de Completude de Turing

Desconsiderando os tamanhos int do C ++ (isto é, supondo que sejam infinitos), -3 é Turing Complete por redução para o cérebro de três células . Posso desconsiderar esse tamanho, pois pode ser gravado um intérprete para -3 em um computador com memória infinita que possui células arbitrariamente grandes.

Eu também acredito que qualquer BCT pode ser escrito como um programa -3.

Conor O'Brien
fonte
Como eu amo a melhorar o meu conteúdo, por favor, uma explicação sobre o voto para baixo seria apreciada
Conor O'Brien