Dicas para jogar golfe no dc


18

Que dicas gerais você tem para jogar golfe no dc ?

dc é um utilitário de calculadora para UNIX / Linux anterior à linguagem C. Estou interessado em como reduzir meus programas de CC (cálculos?). Estou procurando idéias que possam ser aplicadas ao geral que sejam pelo menos um pouco específicas ao dc (por exemplo, remover comentários não é uma resposta útil)

Poste uma dica por resposta.


7
Use Marvel em vez disso.
Magic Octopus Urn

Respostas:


6

Instruções if-then-else

Suponha que desejamos verificar a condição a==b(deixe ae bseja armazenada em seus registros com nomes respectivos).

editar:
[         # Everything is wrapped in one big macro
  [         # An inner macro for our *then* part
              # <-- Stuff to execute if a==b here
  2Q          # Then quit the inner and outer macro
]sE       # `E' is for Execution register ;)
la lb =E  # if a==b, execute E
          # if E is executed, it will quit the whole macro, so the rest is never reached:
          # <-- Stuff to execute if a!=b here
]x        # End macro; Execute

Seja (foo)um espaço reservado, com a finalidade de condensar:

[[(then)2Q]sE(condition)E(else)]x

Certamente essa é a declaração if mais compacta possível (também apresentada aqui ).


1
Talvez [[thenaction]P][[elseaction]P][r]sI 2 4 =I x sI fseja um começo? As ações para tehn e else estão na pilha, a Imacro "f" as troca e é calibrada condicionalmente. o topo da pilha será executado e a macro não utilizada será descartada em I para limpar a pilha. 2 4são apenas os dados de exemplo para comparar. Em alternativa, a [x]sIpeça pode ser movida para a comparação, se considerado mais legível: [[thenaction]P][[elseaction]P] 4 4 [r]sI =I x sI f. A fnos exemplos apenas deve mostrar tat a pilha é limpo depois ...

1
A página de Rosetta Code sobre Dc menciona três sabores dce essa foi a 1ª página em que vi dca if-then-elseconstrução do OpenBSD . Eu acho que precisamos de um dcpacote de fãs com todos os 3 tipos para todos os principais sistemas operacionais ... o :-) ... e minha if-then-elseproposta acima não funciona no original dcporque falta o rcomando ... :-(

1
Que tal: [[(if)2Q]si(condition)i(else)]x- agrupar a coisa toda em uma macro e a parte if dentro de outra macro dentro dela, para que você possa 2Qsair da coisa toda antes de chegar à parte else. Portanto, se você quiser fazer se 1 == 1 e imprimir 1 mais imprimir 2 , seria 1[[1P2Q]si1=i2P]x(não testado, pois não tenho acesso ao dc aqui e agora. Também tinha certeza de que havia feito esse truque em uma resposta aqui antes mas não consegui encontrá-lo)
daniero 17/08/16

Sim, eu fiz as contas, minha sugestão é mais curta. Com o mesmo exemplo e "notação", e removendo o espaço em branco, é [/*else*/]sE[[/*then*/]sE]sIlalb=IlExvs [[/*then*/2Q]sIlalb=I/*else*/]x- diferença de 6 bytes. Ainda tho não testado: P
daniero

1
Bom trabalho, @daniero! Atualizarei a postagem quando tiver tempo, ou você poderá fazê-lo, se quiser.
Joe

5

Você pode salvar a entrada com d

Ao usar d, que duplica os ToS (parte superior da pilha), você pode mover a entrada para uso posterior, enquanto ainda pode usá-la.


@NoOneIsHere oh cool !!! Obrigado!
Rɪᴋᴇʀ

5

Matrizes

Embora sejam uma dor de cabeça para iniciantes, dcoferece matrizes. Eles funcionam assim:

value index :a    # store `value' in array linked to top of stack `a', with index `index'
      index ;a    # push a[index] on (main) stack

Como de costume, o primeiro elemento possui o índice 0. As matrizes podem ser úteis ao trabalhar com sequências, como na sequência SUDSI , especialmente em combinação com contadores. As matrizes podem reduzir a quantidade de embaralhamento de números que você precisa fazer (e o número de contadores e comparações) se desejar selecionar um elemento específico sem destruir o ambiente. Por exemplo, se você quiser mover uma pilha de números para uma matriz, poderá escrever uma função recursiva que use z(profundidade da pilha) ou z 1-como índice, armazene o elemento e verifique se z == 0deve terminar automaticamente.

[z 1- :a z 0 !=F]dsFx    # or I could just write such a function for you :)

Esteja ciente do seguinte:

  • Matrizes são associadas a instâncias em pilhas nomeadas. Se você inserir um novo valor em uma pilha que tenha uma matriz associada, essa matriz também será "empurrada" e uma matriz "nova" será substituída. A matriz antiga não será utilizável até que o valor correspondente na pilha nomeada também seja utilizável (ou seja, no topo da pilha). Este é um conceito complicado que seria melhor explicado com uma boa animação, que está além de mim.
  • Você pode armazenar coisas em uma matriz nomeada sem realmente inserir um valor no registro nomeado correspondente. No entanto, se você fizer isso, não poderá acessar a pilha / registro com esse nome pelo restante da sessão. dcirá falhar.
  • Se você retirar um valor de uma pilha nomeada, quaisquer valores na matriz correspondente serão perdidos - sem avisos, sem salvaguardas, nada. Acabou de ir (o que também pode ser útil).

Bom trabalho com as dicas da DC!
Rɪᴋᴇʀ

dcpode ter sido atualizado recentemente e o comportamento da matriz pode ter sido ligeiramente alterado em relação a falhas. Não posso confirmar agora, mas acho que algo estava diferente da última vez que o usei no Linux.
31417 Joe

1
Se você tentar ler um índice de uma matriz que não foi definida, você obtém 0 e não um erro. O que pode ser muito útil, mas também vale a pena se você estiver colocando 0s em matrizes ... Você precisará de outra maneira de testar se o índice foi tocado.
brhfl 14/06

5

0 à enésima potência em vez de condicionais / macros

Às vezes você pode precisar de algo como ternário:

A == B ? C : D;

Uma boa maneira de lidar com isso é descrita na resposta de @ Joe . No entanto, podemos fazer melhor:

0AB-^E*C+

onde E é D - C.

Isso testa a igualdade aumentando 0 para o poder da diferença dos dois valores. Isso resulta em 1 se igual e 0 em caso contrário. O resto apenas escala 1 ou 0 para os valores C ou D. Isso funciona porquedc fornece 0 0 = 1 e 0 n = 0 para n! = 1.


4

Às vezes é necessário descartar um número da pilha. Uma maneira de fazer isso é simplesmente inseri-lo em uma variável não utilizada, ie st. No entanto, em algumas situações, você pode localizá-lo em alguns outros lugares, por exemplo, na base de entrada quando não houver mais entrada numérica ou no especificador de precisão, se você não tiver mais operações para fazer onde a precisão faria diferença. No primeiro caso, use i. No último caso, use k.


Se a saída numérica não for importante, também opode ser usada. E se alguma dessas coisas não for importante, elas podem ser usadas como armazenamento e apenas como descartá- las - I/ K/ Orecuperá-las respectivamente e salvar bytes sobre sa/ laetc. Valores válidos AFAIK: i2-16; kqualquer número inteiro não negativo; oqualquer número inteiro maior que 1.
brhfl

4

Comprimento Cálculo: Z, X, ez

Zabre o ToS e pressiona o número de dígitos (decimal) se for um número ou o número de caracteres se for uma string. Isso pode ser útil para detectar o comprimento de um resultado (para buffer de saída) ou calcular o comprimento da string. Observe que, para números,Z empurra o comprimento combinado da parte inteira e da parte da fração.

Xexibe os ToS e pressiona o número de dígitos na parte da fração do número. Se o ToS era uma sequência, 0é pressionado.

Para encontrar o número de dígitos na parte inteira do número, pode-se usar dZrX-. Se você não alterou a precisão do padrão k==0, o uso 1/Zé menor, mas suponha que você precise manter uma precisão diferente de zero após a operação:Kr0k1/Zrk é bastante desagradável.

zempurra o número de itens na pilha. Um dos meus comandos favoritos, na verdade não exibe nenhum valor! Pode ser usado para gerar uma sequência de números ou incrementar um contador. Usar zdrepetidamente (digamos, no início de uma macro) pode permitir testar um cálculo de cada número natural ou inteiro em ordem crescente.


Ter utilizado zpara este e que antes, mas nunca ocorreu-me a usá-lo como um hack de um contador ... Excelente ...
brhfl

4

Os dígitos Aa Fserem usados ​​em substituição aos números 10 a 15. No entanto, eles ainda devem ser tratados com eficácia como dígitos da base 10 (assumindo que a base de entrada seja 10) quando em locais diferentes. Em outras palavras, com a base de entrada 10 FFnão representaria 255, representaria(15 * 10) + 15 ou 165.

Na verdade, isso funciona para todos os dígitos 0para Fem qualquer base de entrada 2para 16. Portanto, se a base de entrada for 5, 26Eseria (2 * 5^2) + (6 * 5) + 14ou 94.

Observe que esse comportamento está em vigor para as fontes GNU não modificadas. No entanto, como o @SophiaLechner aponta, as distros baseadas no RedHat parecem usar bc-1.06-dc_ibase.patch que altera esse comportamento para que dígitos> = ibase sejam tratados como ibase - 1, independentemente de seu valor real. Note que o TIO dc parece não ter bc-1.06-dc_ibase.patch (mesmo que seu Fedora 28 ¯_ (ツ) _ / ¯).


Isso não está certo - embora dígitos únicos acima da base de entrada sejam interpretados como você esperaria, se o literal tiver vários dígitos, ou mesmo um ponto decimal, dígitos inválidos para a base serão interpretados como (base-1). Assim, na base de entrada 10 FFrepresenta 99, na base de entrada 5 26Eé o mesmo que 244, ou seja, base 10 74.
Sophia Lechner

@SophiaLechner Você tem certeza? tio.run/##S0n@/9/QIJ/L0CCTy82tgMs0k8vIzLXg/38A Qual dcversão você está executando? Eu estou usando o GNU dc 1.4.1 no ubuntu e o GNU dc 1.3 no MacOS
Digital Trauma

Interessante. Estou executando o 1.3.95 no Red Hat, e aqui está o seu programa de exemplo: [slechner @ XXX] $ dc -e '10o 10i FFp 5i 26Ep' 99 74 [slechner @ XXX] $ dc --version dc (GNU bc 1.06 .95) 1.3.95
Sophia Lechner

Argh ... não pode fazer o bloco de código funcionar no comentário. O ponto é que as FFpsaídas são 991.3.95. Você poderia verificar isso na sua versão do MacOS?
Sophia Lechner

1
Coisa certa! Obrigado por toda a pesquisa.
Sophia Lechner

2

Ao inicializar uma macro de função (que usaremos F) que você deseja executar imediatamente, use algo como em dsFxvez de sFlFx. O mesmo funciona para variáveis: em dsavez desala .

Se você precisar fazer outras coisas entre o armazenamento e o carregamento (por exemplo, sa[other stuff]la ), considere ainda a viabilidade acima: Se você deixar um valor na pilha antes das outras operações, ele voltará ao topo no final dessas operações?


2

Acabei de descobrir isso por acidente. Ainda outra maneira de gerar um zero:_ .

_é um sinal para dc de que os dígitos seguintes são um número negativo. Exemplo:

_3 # pushes -3

Mas e se não o seguirmos com um número?

_ # pushes 0...sometimes

Isso funciona quando o próximo caractere que não estiver em branco após o sublinhado não for um dígito. Se um dígito o seguir, mesmo após uma nova linha, ele será interpretado como um sinal negativo.

c4 5_6  # -6,5,4
c4 5_ 6 # -6,5,4
c4 5_
6       # -6,5,4 # still a negative sign since the next thing it sees is a digit
c4 5_z  #  3,0,5,4 # if it's followed by a non-digit, it's a 0
c4 5_p6 #  6,0,5,4
c4 _*   #  0 # 4*0=0

1

Se o conteúdo de toda a pilha precisar ser impresso no final de um programa, um loop macro recursivo poderá ser usado para isso. No entanto, é muito mais curto simplesmente usar o fcomando.


1

dclê a entrada de uma linha de cada vez. Se você precisar ler vários itens, fazer uma por linha exige uma? para cada linha a ser lida ou um loop macro pesado. Em vez disso, se todos os itens de entrada puderem ser colocados em uma linha separada por espaço, um único? lerá todos os itens de entrada, empurrando cada um deles para a pilha.

Por exemplo em seq 10 | dc -e'?f', seqgera números inteiros 1 a 10, um por linha. o ?irá apenas ler o primeiro 1que será produzido quando fdespejar toda a pilha. No entanto seq 10 | tr '\n' ' ' | dc -e'?f', trfaz com que os números inteiros de entrada sejam separados por todo o espaço. Nesse caso, ?ele lerá todos os números inteiros da linha de uma só vez e fexibirá todos eles.


1

Se um operador estiver restrito da fonte, faça um novo com a

Algo que já me foi útil algumas vezes agora é evitar o uso de um operador específico, pressionando o valor ASCII do operador, aconvertendo-o em uma cadeia de caracteres e sinserindo-o em um registro para ser executado como uma macro posteriormente em. Por exemplo, preciso fazer divisão, mas sou proibido de tentar evitar o uso do personagem /. Em vez disso, eu posso fazer 47asde, no futuro, quando precisar dividir 16 por 4 16 4 ldx,.

  • Isso funcionará apenas para operadores de caractere único (não é possível criar uma string) e não funcionará para comandos como s esse que precisam ser postfixados por alguma coisa.
  • Isso adiciona alguns bytes e, portanto, é adequado apenas quando é necessário evitar o caractere específico ou, de alguma forma, proporcionar um bônus de pontuação.

1

Evitando espaço em branco

Evitar o espaço em branco surge em alguns desafios e geralmente é fácil dc. Além de cordas, um momento muito específico que os espaços em branco se torna necessário é quando empurrando vários números consecutivas: 1 2 3. Se isso deve ser evitado:

  • Executar um macro vazio entre: 1[]x2[]x3[]x.
  • Se os suportes estão fora da mesa, armazenar um NOP de uma frente macro de tempo: 35asne executar -lo no meio: 1lnx2lnx3lnx.

Você também pode vírgula com números separados, se estiver disposto a aceitar dc: ',' (054) unimplementedavisos.
Digital Trauma

Eu não tinha pensado nisso - presumivelmente isso se aplica a qualquer dado dado que não resolva um comando… interessante…
brhfl
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.