Quantas Guinés em um total de três centavos?

32

Até a decimalização em 1971 , o dinheiro britânico era baseado na divisão da libra em 240 centavos. Um xelim custava 12 centavos, então 20 xelins ganhavam uma libra. A menor denominação era a de um quarto de centavo. Havia muitas outras denominações e apelidos para moedas, o que pode ser bastante confuso se você não estiver acostumado ao sistema.

Desafio

Escreva um programa ou função que possa converter (quase) qualquer denominação do antigo dinheiro inglês em qualquer outro. Para tornar mais fácil para o usuário, você precisa suportar plurais e apelidos.

Essas são as denominações e seus termos sinônimos que você deve apoiar. Por conveniência, seu valor em peões leva cada linha.

1: farthing, farthings
2: halfpence, halfpenny, halfpennies
4: penny, pennies, pence, copper, coppers
8: twopenny, twopennies, twopence, tuppence, half groat, half groats
12: threepence, threepenny, threepennies, threepenny bit, threepenny bits, thruppence, thrupenny, thrupennies, thrupenny bit, thrupenny bits
16: groat, groats
24: sixpence, sixpenny, sixpennies, sixpenny bit, sixpenny bits, tanner, tanners
48: shilling, shillings, bob
96: florin, florins, two bob bit, two bob bits
120: half crown, half crowns
240: crown, crowns
480: half sovereign, half sovereigns
504: half guinea, half guineas
960: pound, pounds, pounds sterling, sovereign, sovereigns, quid, quids
1008: guinea, guineas

(Eu não sou britânico, esta lista não é de modo algum autoritária, mas será suficiente para o desafio.)

Via stdin ou argumento de função, você deve usar uma string do formulário

[value to convert] [denomination 1] in [denomination 2]

e devolver ou imprimir

[value to convert] [denomination 1] is [converted value] [denomination 2]

onde [converted value]é [value to convert]unidades de denominação 1 convertido para denominação 2.

O [value to convert]e [converted value]são flutuadores positivos. Na saída, ambos devem ser arredondados ou truncados com 4 casas decimais. Se desejar, você pode assumir [value to convert]sempre que tem um ponto decimal e zero ao inserir (por exemplo, em 1.0vez de 1).

As denominações 1 e 2 podem ser quaisquer dois termos da lista acima. Não se preocupe se são plurais ou não, trate todas as denominações e sinônimos da mesma forma. Você pode assumir que o formato e as denominações de entrada são sempre válidos.

Exemplos

1 pounds in shilling1 pounds is 20 shilling
(tudo 1.0000 pounds is 20.0000 shillingbem)

0.6 tuppence in tanner0.6 tuppence is 0.2 tanner

24 two bob bits in pounds sterling24 two bob bits is 2.4 pounds sterling

144 threepennies in guineas144 threepennies is 1.7143 guineas

Pontuação

O código mais curto em bytes vence.

Passatempos de Calvin
fonte
11
"moedas de um centavo" é usado apenas para se referir a um número de moedas, não a uma quantidade de dinheiro.
precisa saber é o seguinte
4
Nit-pick: após a decimalização, o plural de quidé quid. Provavelmente isso teria sido o mesmo com o dinheiro antigo. Exemplo: Five quid a pint! Cor blimey guvnor. Exceção: quids-in
Digital Trauma
7
Eu provavelmente bagunçaria muitas pessoas exigindo que elas incluíssem "ha'penny".
kaine
3
Nunca ouvi um galinheiro chamar nada além de galinheiro, @kaine. Como em en.wikipedia.org/wiki/Ha%27penny_Bridge . É claro que sou jovem demais para ouvi-lo com frequência, mas o apóstrofo parece padrão na escrita.
TRiG

Respostas:

9

Pyth , 146 145

K4J24L?*y>b-5>b\t?2>b\t.5}<b2"hatw"@[1K8K12K16J48J1008*JT96*2J960)xc"fapetucothengrsishtagucrflbo"2<b2AGHcz" in"++G%" is %0.4f"*vhcGdcyjdtcGdytHH

Mais legível (novas linhas e recuos devem ser removidos para serem executados):

K4J24
L?*y>b-5>b\t?2>b\t.5
  }<b2"hatw"
  @[1K8K12K16J48J1008*JT96*2J960)
   xc"fapetucothengrsishtagucrflbo"2<b2
AGHcz" in"
++G
  %" is %0.4f"
   *vhcGdcyjdtcGdytH
 H

Atualização: Acontece que é 1 caractere menor (não é necessário espaço) para dividir a string em uma lista de 2 strings antes de executar a operação de índice de strings. /x"string"<b2 2-> xc"string"2<b2. Nada mais precisa ser mudado.

Como funciona:

  • Isso usa a abordagem do @ xnor de procurar o valor da moeda usando suas duas primeiras letras, bem como o truque de detectar a inicial halfou tworemovê-la e chamar a função novamente.

  • Para pesquisar o valor dos dois primeiros caracteres, ele localiza o local das duas primeiras letras da moeda em uma sequência de caracteres, depois divide por 2 e assume o valor nesse índice na lista. Isso é muito mais curto que um ditado em pyth.

  • Usa o fato de que x(localizar na sequência de caracteres) retorna -1 em caso de falha para evitar colocar po(libras) qu(quid) ou so(soberanos) na sequência e simplesmente retorna o último elemento da lista, 960, por padrão.

  • Reorganizando a ordem das moedas no sistema de pesquisa e inicializando com cuidado, com K4e J24, todos os espaços que seriam necessários para separar números na lista foram removidos.

  • Usa o operador de atribuição dupla do pyth,, Ana divisão de entrada inpara obter o início e o fim da entrada em variáveis ​​separadas.

  • Faz essencialmente a mesma pesquisa no final, embora pyth não tenha .split(_,1), por isso é um pouco mais complicado.

Exemplos:

$ pyth programs/currency.pyth <<< '5 florins in half guineas'
5 florins is 0.9524 half guineas

$ pyth programs/currency.pyth <<< '0.4 quid in sixpenny bits'
0.4 quid is 16.0000 sixpenny bits
isaacg
fonte
3
Eu desisto ...;)
Martin Ender
Eu não conhecia <e >trabalhei como operadores de string / list slice; isso é muito, muito melhor do que tomar na cabeça ou no final de uma costeleta :)
FryAmTheEggman
@FryAmTheEggman Parece que também estava faltando na documentação - eu a adicionei.
Isaacg
Eu provavelmente deveria ler macros.py mais cuidado :)
FryAmTheEggman
14

Ruby, 345 306 302 288 287 278 273 253 252 242 232 221 202 190 bytes

f=->s{" !#+/7OďǿȗϟЏ'"[%w{fa fp ^pe|co r..p ^gr x|ta sh|^b fl|b f.c ^c f.s .gu d|v ^g .}.index{|k|s[/#{k}/]}].ord-31}
$><<gets.sub(/ (.+ i)n /){" #{r=$1}s %0.4f ".%$`.to_f/f[$']*f[r]}

Pega a entrada de STDIN e imprime em STDOUT.

Estou usando expressões regulares curtas para corresponder apenas às denominações desejadas para cada valor. Existem duas matrizes, uma com expressões regulares e outra com valores, nos índices correspondentes. A matriz regex é uma literal de matriz delimitada por espaço e a matriz de valores é compactada em uma sequência de caracteres UTF-8.

Estou selecionando o índice nos valores pesquisando por uma regex que corresponda a cada denominação. Também estou padronizando o caso tuppence / half-groat (valor 8), porque isso exigia a maior expressão regular. Da mesma forma, alguns padrões assumem que outros valores já foram correspondidos por padrões anteriores, portanto, cada regex distingue apenas o valor desejado apenas dos demais. Usando isso, eu provavelmente poderia economizar mais alguns bytes reorganizando a ordem das denominações.

Agradeço ao Ventero por me ajudar a vencer o Pyth, tornando-o mais curto!

Martin Ender
fonte
11
É a correspondência de expressões regulares ( s[k]) que substitui $1etc. Você pode salvar alguns caracteres movendo o bloco de mapa para um lambda e chamando isso diretamente na última linha (que também permite que você descarte as atribuições de $1e $2). Também .indexé mais curto que .find_index.
Ventero 3/11
@ Ventero Ah, isso faz sentido. Obrigado!
10137 Martin Ender
11
Regexp.new k/#{k}/e $><<gets.sub(/foo/){a=$3;...}gets[/foo/];a=$3;puts...para um total de 221. E é claro que você pode usar o velho truque de empacotar a matriz int em uma string (using .pack("U*")) e depois indexar nessa string. Você deve reduzir para 195 chars / 200 bytes.
Ventero 4/11
Ainda melhor:a=gets[/foo/,3]
Ventero 4/11/2014
@Ventero Muito obrigado. Acabei com 196/202, porque adicionei um deslocamento aos códigos de caracteres para evitar ASCII não imprimível. Ainda mais baixo que Pyth. ;)
Martin Ender
8

Python 3: 264 239 caracteres

f=lambda c:c[:2]in"hatw"and f(c[5-(c>'t'):])*2/4**(c<'t')or[1,4,4,4,8,12,16,24,24,48,48,96,240,1008,960]['fapecoentuthgrsitashboflcrgu'.find(c[:2])//2]
a,b=input().split(" in ")
x,c=a.split(" ",1)
print(a,"is %0.4f"%(eval(x)*f(c)/f(b)),b)

A função fobtém o valor de shilling da string da moeda c, imprimindo as duas primeiras letras usando o dicionário , encontrando-as em uma string. Os prefixos "half" e "two" são detectados e contabilizados cortando o prefixo e o espaço e aplicando um multiplicador. Como "halfpenny" não possui espaço após "half", isso resulta em "enny", mas isso é tratado com uma entrada fictícia "en".

Obrigado a @isaacg e @grc por muitas melhorias na pesquisa de dicionário.

xnor
fonte
Eu sabia que poderia ser feito :) Também estou extremamente envergonhada por não saber que você poderia definir um dicionário como esse ...: S
FryAmTheEggman
2
@FryAmTheEggman Também não foi possível definir dicionários por meio de palavras-chave até que eu o vi usado em uma resposta neste site. As coisas que você aprende golfe ...
xnor
Eu fiz uma versão Pyth disso e obtive 207 caracteres. Você gostaria de publicá-lo aqui para adicionar ou publicar uma resposta do wiki da comunidade?
FryAmTheEggman
11
+1 para essa 2/4**(c<'t')parte.
Njzk2
11
Você pode salvar 13 caracteres usando .get(c[:2],960)para pesquisar o valor do dicionário e omitir as po=960,so=960,qu=960,entradas do dicionário.
Isaacg #
5

Python 2 - 345 358

s=str.startswith
h='half'
u,v=raw_input().split(' in ')
a,b=u.split(' ',1)
C=dict(fa=1,pe=4,twop=8,tu=8,thr=12,gr=16,si=24,ta=24,sh=48,b=48,fl=96,c=240,po=960,so=960,q=960,gu=1008)
C.update({h+'p':2,h+' gr':8,'two ':96,h+' c':120,h+' s':480,h+' gu':504})
for c in iter(C):
 if s(b,c):k=C[c]
 if s(v,c):f=C[c]
print u+' is %0.4f '%(eval(a)*k/f)+v

Requer que o número de entrada seja um float em python, isto é 144.1

Eu acho que isso pode ser reduzido em python 3 ...

... Confirmado graças a @xnor. Também confirmou que ter um algoritmo melhor é muito importante;)

FryAmTheEggman
fonte
Eu substitui q=raw_input().split(' in ')porq,b=raw_input().split(' in ')
njzk2
@ njzk2 Muito bem ... Eu também usei isso para a próxima linha, agora :)
FryAmTheEggman 3/14
Eu acho que existe um conflito entre h+' gr':8e h+' g':504dependendo de quem é avaliado primeiro por meio grumos
njzk2
@ njzk2 isso é verdade ... adicionado uao um guinéu ...
FryAmTheEggman
2

Haskell - 315 bytes

w x=f(u x)*v(u x)
f=maybe 1 id.l"ha tw tu th si"[0.5,2,2,3,6]
v x@(_:xs)|Just w<-l"bo cr gr gu so co fa fl pe po qu sh ta"[12,60,4,252,240,1,0.25,24,1,240,240,12,6]x=w|True=v xs
l k v x=take 2 x`lookup`zip(words k)v
u=unwords
i s|(n:x,_:t)<-span(/="in")$words s=u$n:x++["is",show$read n*w x/w t]++t
main=interact i
Rhymoid
fonte
2

JavaScript (ES5), 344

I=prompt()
n=I.match(/[\d.]+ /)[0]
A=I.slice(n.length).split(" in ")
function m(x){return{fi:1,he:2,p:4,pe:4,cr:4,tn:8,hg:8,tp:12,te:12,g:16,gs:16,sn:24,tr:24,si:48,b:48,fn:96,to:96,hc:120,c:240,cs:240,hs:480,hgtrue:504,ps:960,se:960,q:960,ga:1008}[x[0]+(x[5]||"")+(x[10]=="a"||"")]}
alert(n+A[0]+" is "+(n*m(A[0])/m(A[1])).toFixed(4)+" "+A[1])

Fui com uma abordagem de função hash ... Acho que subestimei (relativamente) o quão complexo seria o processamento de entrada (sobre a abordagem regex, que não se importaria com o número).

FireFly
fonte
1

Com base na resposta de @ FryAmTheEggMan, com uma maneira diferente de testar str.startwith:

Python 2: 317

h='half'
C=dict(fa=1,pe=4,twop=8,tu=8,thr=12,gr=16,si=24,ta=24,sh=48,b=48,fl=96,c=240,po=960,so=960,q=960,gu=1008)
C.update({h+'p':2,h+' gr':8,'two ':96,h+' c':120,h+' s':480,h+' gu':504})
u,v=raw_input().split(' in ')
a,b=u.split(' ',1)
s=lambda x:x and C.get(x, s(x[:-1]))
print u+' is %0.4f '%(eval(a)*s(b)/s(v))+v
njzk2
fonte
Eu acho que você precisa adicionar um espaço à direita printna string formatada. Você também pode reescrever o lambda s=lambda x:x and C.get(x,s(x[:-1]))or 0para salvar um caractere (junto com os espaços). Esta é uma grande idéia, btw :)
FryAmTheEggman
obrigado, brinquei por um tempo com essa notação ternária, que sempre acho detalhada, mas não pensei nisso and/or.
Njzk2
Sim, eu aprendi aqui :) Eu também acho que você precisa u.split(' ')dizer algo u.split(' ',1)para moedas que têm espaços, como "meio soberano".
FryAmTheEggman #
então essa é a razão para o , 1!
precisa saber é o seguinte
2
O ternário x and y or 0pode ser encurtado em geral para x and y, uma vez que ambos avaliam 0ou equivalentemente Falsequando xé Falsey.
Xnor
1

JavaScript ES6, 264 273

f=t=>{s=t.split(x=' in')
c=d=>{'t0sh|bo0^p|co0f0fp0fl|b b0gu0d|v0wn0gr0f g|t..?p0f s0f gu0f c0x|an'.split(0).map((e,i)=>{v=s[d].match(e)?[12,48,4,1,2,96,1008,960,240,16,8,480,504,120,24][i]:v})
return v}
return s.join(' is '+~~(1e4*t.split(' ')[0]*c(0)/c(1))/1e4)}

Isso obtém o valor de cada moeda, comparando-a com várias regexes, começando pela mais ampla /t/; o valor será substituído se outra correspondência for encontrada. Pode haver uma maneira de economizar alguns bytes reordenando a string regex. Você pode testá-lo usando o trecho de código acima (ele está formatado apenas para usar caixas de diálogo e remover as funções de seta ES6 para que todos possam testar o código facilmente). Obrigado a Alconja pelas sugestões.

NinjaBearMonkey
fonte
11
Você pode aparar 2 caracteres usando 't0sh|bo0^p....'.split(0), 4 mais, usando .map, em vez de .forEache 3 mais chamando c(0)e c(1)e fazendos[d].match
Alconja