Número de teléfono en palabras habladas

33

Gol

Escriba un programa o función que traduzca un número de teléfono numérico en texto que lo haga fácil de decir. Cuando se repiten los dígitos, deben leerse como "doble n" o "triple n".

Requisitos

Entrada

Una cadena de dígitos.

  • Suponga que todos los caracteres son dígitos del 0 al 9.
  • Suponga que la cadena contiene al menos un carácter.

Salida

Palabras, separadas por espacios, de cómo estos dígitos pueden leerse en voz alta.

  • Traducir dígitos a palabras:

    0 "oh"
    1 "uno"
    2 "dos"
    3 "tres"
    4 "cuatro"
    5 "cinco"
    6 "seis"
    7 "siete"
    8 "ocho"
    9 "nueve"

  • Cuando el mismo dígito se repite dos veces seguidas, escriba " número doble ".

  • Cuando el mismo dígito se repite tres veces seguidas, escriba " número triple ".
  • Cuando el mismo dígito se repite cuatro o más veces, escriba " número doble " para los primeros dos dígitos y evalúe el resto de la cadena.
  • Hay exactamente un carácter de espacio entre cada palabra. Un único espacio inicial o final es aceptable.
  • La salida no distingue entre mayúsculas y minúsculas.

Tanteo

Código fuente con la menor cantidad de bytes.

Casos de prueba

input        output
-------------------
0123         oh one two three
4554554      four double five four double five four
000          triple oh
00000        double oh triple oh
66667888     double six double six seven triple eight
19999999179  one double nine double nine triple nine one seven nine
Hand-E-Food
fuente
38
Cualquier persona interesada en el "golf de discurso" debe tener en cuenta que "seis dobles" lleva más tiempo para decir que "seis seis". De todas las posibilidades numéricas aquí, solo "triple siete" guarda las sílabas.
Púrpura
13
@Purple P: Y como estoy seguro de que sabes, 'double-u double-u double-u'> 'world wide web' ...
Chas Brown
11
Voto para cambiar esa carta a "dub".
Hand-E-Food
8
Sé que esto es solo un ejercicio intelectual, pero tengo frente a mí una factura de gas con el número 0800 048 1000, y lo leería como "oh ochocientos oh cuatro ocho mil". La agrupación de dígitos es importante para los lectores humanos, y algunos patrones como "0800" se tratan especialmente.
Michael Kay
3
@PurpleP Cualquier persona interesada en la claridad del habla, sin embargo, especialmente cuando habla por teléfono, puede querer usar "doble 6", ya que es más claro que el hablante significa dos seises y no repitió el número 6 accidentalmente. Las personas no son robots: P
Disculpe y reinstale a Mónica el

Respuestas:

10

05AB1E , 53 52 51 50 49 bytes

γε€T2äθ¬MÊi¨₃1ǝR]˜“Šç€µ‚•„í†ìˆÈŒšï¿Ÿ¯¥Š‹¶½¿“#s踻

Pruébalo en línea!

Explicación:

γ                      # split input in groups of consecutive equal digits
 ε              ]      # for each group
  €T                   #  add a 10 before each digit (66 -> [10, 6, 10, 6])
    2äθ                #  keep only the second half of that list
       ¬MÊi     ]      #  if the first element is not the maximum
           ¨           #   drop the last element
            ₃1ǝ        #   replace the second element with 95
               R       #   reverse the list
˜                      # flatten
 “...“                 # compressed string: "oh one two ... nine double triple"
      #                # split on spaces
       sè              # index (wraps around, so 95 yields "triple")
         ¸»            # join with spaces
Mugriento
fuente
1
Ah, ¿ Mtambién mira dentro de las listas al determinar el número entero máximo en la pila? No lo sabia. Suena como algo para recordar. :)
Kevin Cruijssen
16

8088 Assembly, IBM PC DOS, 164 159 156 155 bytes

Binario:

00000000: d1ee 8a0c 03f1 53fd ac3a d075 0343 e2f7  ......S..:.u.C..
00000010: 85db 741c 5f8a d043 f6c3 0174 0a57 bd64  ..t._..C...t.W.d
00000020: 0155 83eb 0374 0957 bd5d 0155 4b4b 75f7  .U...t.W.].UKKu.
00000030: 8ad0 2c2f 7213 518a f0b0 24b1 31bf 6a01  ..,/r.Q...$.1.j.
00000040: fcf2 aefe ce75 fa59 57e2 bc5a 85d2 740c  .....u.YW..Z..t.
00000050: b409 cd21 b220 b402 cd21 ebef c364 6f75  ...!. ...!...dou
00000060: 626c 6524 7472 6970 6c65 246f 6824 6f6e  ble$triple$oh$on
00000070: 6524 7477 6f24 7468 7265 6524 666f 7572  e$two$three$four
00000080: 2466 6976 6524 7369 7824 7365 7665 6e24  $five$six$seven$
00000090: 6569 6768 7424 6e69 6e65 24              eight$nine$

Construya y pruebe el ejecutable usando xxd -rdesde arriba, o descargue PHONE.COM .

Listado sin ensamblar:

D1 EE       SHR  SI, 1              ; point SI to DOS PSP (80H) for input string
8A 0C       MOV  CL, BYTE PTR[SI]   ; load input string length into CX
03 F1       ADD  SI, CX             ; move SI to end of input 
53          PUSH BX                 ; push a 0 to signal end of output stack 
        CHAR_LOOP:
FD          STD                     ; set LODS direction to reverse 
AC          LODSB                   ; load next char from [SI] into AL, advance SI 
3A D0       CMP  DL, AL             ; is it same as previous char? 
75 03       JNZ  NEW_CHAR           ; if not, it's a different char 
43          INC  BX                 ; otherwise it's a run, so increment run length
E2 F7       LOOP CHAR_LOOP          ; move on to next char 
        NEW_CHAR: 
85 DB       TEST BX, BX             ; is there a run greater than 0? 
74 1C       JZ   GET_WORD           ; if not, look up digit name 
5F          POP  DI                 ; get name for the current digit 
8A D0       MOV  DL, AL             ; save current char in DL 
43          INC  BX                 ; adjust run count (BX=1 means run of 2, etc)
F6 C3 01    TEST BL, 1              ; is odd? if so, it's a triple
74 0A       JZ   IS_DBL             ; is even, so is a double 
57          PUSH DI                 ; push number string ("one", etc) to stack
BD 0164     MOV  BP, OFFSET T       ; load "triple" string 
55          PUSH BP                 ; push to stack 
83 EB 03    SUB  BX, 3              ; decrement run count by 3 
74 09       JZ   GET_WORD           ; if end of run, move to next input char 
        IS_DBL: 
57          PUSH DI                 ; push number string to stack
BD 015D     MOV  BP, OFFSET D       ; load "double" string 
55          PUSH BP                 ; push to stack 
4B          DEC  BX                 ; decrement by 2
4B          DEC  BX
75 F7       JNZ  IS_DBL             ; if not end of run, loop double again 
        GET_WORD: 
8A D0       MOV  DL, AL             ; save current char into DL
2C 2F       SUB  AL, '0'-1          ; convert ASCII char to 1-based index 
72 13       JB   NOT_FOUND          ; if not a valid char, move to next
51          PUSH CX                 ; save outer loop counter 
8A F0       MOV  DH, AL             ; DH is the index to find, use as scan loop counter
B0 24       MOV  AL, '$'            ; word string is $ delimited
B1 31       MOV  CL, 031H           ; search through length of word data (49 bytes)
BF 016A     MOV  DI, OFFSET W       ; reset word data pointer to beginning
FC          CLD                     ; set DF to scan forward for SCAS 
        SCAN_LOOP: 
F2/ AE      REPNZ SCASB             ; search until delimiter '$' is found in [DI]
FE CE       DEC  DH                 ; delimiter found, decrement counter 
75 FA       JNZ  SCAN_LOOP          ; if counter reached 0, index has been found 
59          POP  CX                 ; restore outer loop position
57          PUSH DI                 ; push string on stack 
        NOT_FOUND:
E2 BC       LOOP CHAR_LOOP          ; move to next char in input 
        OUTPUT_STACK: 
5A          POP  DX                 ; get string from top of stack 
85 D2       TEST DX, DX             ; it is the last? 
74 0C       JZ   EXIT               ; if so, exit 
B4 09       MOV  AH, 09H            ; DOS display string function 
CD 21       INT  21H                ; write string to console 
B2 20       MOV  DL, ' '            ; load space delimiter 
B4 02       MOV  AH, 02H            ; DOS display char function 
CD 21       INT  21H                ; write char to console 
EB EF       JMP  OUTPUT_STACK       ; continue looping 
        EXIT: 
C3          RET                     ; return to DOS 

D   DB "double$" 
T   DB "triple"
W   DB "$oh$","one$","two$","three$","four$","five$","six$","seven$","eight$","nine$" 

TL; DR:

La cadena de entrada se lee de derecha a izquierda para que sea más fácil encontrar un triple. La salida se inserta en la pila x86 para simplificar la inversión del orden de visualización y también facilita la reorganización de las palabras "doble" y "triple" para preceder al nombre del dígito.

Si el siguiente dígito es diferente al último, el nombre se busca en la lista de palabras y se pasa a la pila. Como no hay un concepto formal de una "matriz indexada de cadenas de longitud variable" en el código de máquina, la lista de palabras se escanea i(el índice de la palabra) varias veces para que el delimitador de cadena ( $) encuentre la palabra correspondiente. Con ayuda, x86 tiene un par de instrucciones cortas ( REPNZ SCASBque es similar a la memchr()de C), que simplifica esto (¡gracias CISC !).

Si el dígito es el mismo que el anterior, el contador para la duración de una "ejecución" se incrementa y continúa girando hacia la izquierda en la entrada. Una vez que finaliza la ejecución, el nombre del dígito se toma de la pila, ya que deberá colocarse después del "doble" o "triple" para cada grupo. Si la longitud de la ejecución es impar (y la longitud de la ejecución es > 1), el nombre del dígito seguido de la cadena "triple" se empuja a la pila y la longitud de la ejecución se reduce en 3. Dado que la longitud de la ejecución ahora será par, el paso se repite para "doble" hasta que la longitud de ejecución es 0.

Cuando la cadena de entrada ha llegado al final, la pila se descarga con cada cadena guardada escrita en la pantalla en orden inverso.

E / S:

Un ejecutable independiente de PC DOS, entrada desde la salida de línea de comandos a la consola.

enter image description here

Descargue y pruebe PHONE.COM .

640 KB
fuente
repne scasbes memchr(o strchrsi sabes que va a haber un éxito), no strstr.
Peter Cordes
¿CH = 0 en la entrada del proceso está garantizado por un estándar, o es eso lo que hacen algunas versiones de DOS? Me doy cuenta de que asumes que mov cl, byte[si] es equivalente a movzx cx, byte [si]. Me pregunto si usar un registro diferente, como AH, para el conteo en dec ah / jnzlugar de loopahorraría algo de no tener que presionar / hacer estallar CX. Probablemente no, y no le quedan registros de 16 bits que permitan 1 byte dec.
Peter Cordes
1
@PeterCordes, para CH=0ir por fysnet.net/yourhelp.htm , que para cualquier versión razonable de DOS siempre se pone a cero, lo mismo con BX. Bien pensado sobre la extensión cero mov, aunque técnicamente no creo que movzxesté disponible en el 808x (manteniendo la plataforma de destino como una PC 5150 de IBM y todo). Jugueteé con todos los registros lo mejor que pude para guardar los bytes, pero si ves algo que probablemente me perdí, ¡házmelo saber!
640 KB
1
Es más preciso llamarlo memchrIMO. El nombre de "instrucción de cadena" induce a error a las personas a pensar que trabajan en cadenas C de longitud implícita, pero realmente funcionan en cadenas de longitud explícita como std::stringo buffers. Como memcpy, memset(mov / stos), memchr/ memrchr(repne scas con DF = 0 o 1), y memcmp(repe cmps). El único equivalente de C repe scases strspnporque no creo que haya una memfunción para eso. Incluso puedes describir stoswo stosdcomo wmemsetpor ejemplo.
Peter Cordes
1
movzxcuesta un byte de código de operación adicional, y sí, solo se introdujo con 386. Simplemente fue más fácil escribir para describir el hecho de que está haciendo una combinación de bajo byte y suponiendo que está correctamente extendido a cero. Si conoce CX o al menos CH = 0, entonces sí para jugar al golf siempre vaya mova CL. Pero fuera del golf, las instrucciones de carga de bytes de x86 son movzxy movsx: evitan cualquier dependencia falsa u otras travesuras de registro parcial. En las CPU modernas con un destino dword, son tan rápidas como se mov carga dword .
Peter Cordes
9

05AB1E , 61 56 53 52 51 bytes

γvyDg;LàäRv… ‹¶½¿#yg蓊瀵‚•„í†ìˆÈŒšï¿Ÿ¯¥Š“#yè])áðý

-9 bytes gracias a @Grimy .

Pruébelo en línea o verifique todos los casos de prueba .

Explicación:

γ               # Split the (implicit) input into substrings of equal adjacent characters
                #  i.e. "199999991779" → ["1","9999999","1","77","9"]
 v              # Loop over each substring `y`:
   Dg           #  Get the length of a copy of the substring
     ;          #  Halve it
      L         #  Create a list in the range [1, length/2], where odd lengths are
                #  automatically truncated/floored
                #   i.e. "1" (length=1) → 0.5 → [1,0]
                #   i.e. "9999999" (length=7) → 3.5 → [1,2,3]
       à        #  Pop and push the maximum of this list
  y     ä       #  Divide the string into that many parts
                #   → ["1"]
                #   → ["999","99","99"]
         R      #  Reverse the list
                #   → ["99","99","999"]
  v             # Inner loop over each item `y`:
    ‹¶½¿       #  Push dictionary word: "  double triple"
         #      #  Split it on spaces: ["","","double","triple"]
          yg    #  Get the length of the current item `y`
            è   #  And use it to (0-based) index into the list
   “Šç€µ‚•„í†ìˆÈŒšï¿Ÿ¯¥Š“
                #  Push dictionary string "oh two three four five six seven eight nine"
     #          #  Split it on spaces: ["oh","two","three",...,"nine"]
      yè        #  Use `y` to index into the string-list (with automatic wrap-around,
                #  so since there are 10 words, it basically indexes with a single digit
                #  due to an implicit modulo-10)
                #   i.e. "77" → "seven"
 ]              # Close both loops
  )             # Wrap all values on the stack into a list
   á            # Only keep letters, which removes the empty strings from the list
    ðý          # And join the list on spaces
                # (after which the result is output implicitly)

Vea esta sugerencia mía 05AB1E (sección ¿Cómo usar el diccionario? ) Para comprender por qué … ‹¶½¿es " double triple"y “Šç€µ‚•„í†ìˆÈŒšï¿Ÿ¯¥Š“es "oh two three four five six seven eight nine".

Kevin Cruijssen
fuente
1
@Grimy Ah, por supuesto ... agregué el if(length>=4)antes de agregar el resto, pero por supuesto no es necesario para enteros de tamaño 1,2,3, porque ;Å2¨3ª£dejarán las cadenas intactas (simplemente envueltas en una lista que aplastamos después del mapa de todos modos). ¡Gracias por notarlo! Y esperando ver tu respuesta con Åγ. De hecho, tuve la sensación de que la primera parte podría hacerse mucho más corta de alguna manera.
Kevin Cruijssen
1
Dg;LàäRsigue siendo un byte más corto āɨšJõKy mucho más similar al que tenía originalmente.
Grimmy
1
@Grimy Ah, eso está realmente cerrado a lo que inicialmente tuve, me gusta. :) ¡Gracias de nuevo!
Kevin Cruijssen
1
@Grimy Pude encontrar un campo de golf más que olvidé ... en álugar de õKal final. :)
Kevin Cruijssen
1
Bonito hallazgo con á! Aquí hay un 51 y otro . 50 se siente posible.
Grimmy
7

QuadR , SBCS de 137 bytes

Título de caso con un espacio principal.

∊¯2↑¨@(∊∘⎕A)⍵
(.)\1*
{⍺←(,¨⎕D)⎕R('OhOneTwoThreeFourFiveSixSevenEightNine'(∊⊂⊣)⎕A)⋄' 'w←⍺,⊃⍵:⍬⋄1=≢⍵:⍺⍵⋄3=≢⍵:'Triple',w'Double',w,∇2↓⍵}⍵M

Pruébalo en línea!

ε nlist (aplanar)
¯2↑¨ tomar los dos últimos caracteres (relleno a la izquierda con un espacio) de cada uno de los personajes
@ en posiciones en las
(∊∘⎕A)  los personajes son miembros de la mayúscula A lphabet
 en el resultado de la operación por debajo de PCRE reemplazar ...

(.) cualquier personaje
\1  seguido por sí mismo
* cero o más veces, se reemplaza por el resultado de lo siguiente ...

{…}⍵M "dfn"; es el M atch del patrón anterior

('OhOneTwoThreeFourFiveSixSevenEightNine'(...)⎕A)  Aplique la siguiente función tácita anónima con la cadena larga y el alfabeto A mayúscula como argumentos izquierdos:

 membresía (de letras en la cadena larga en el alfabeto en mayúscula)

 particiones (con una nueva partición que comienza cada vez que es un miembro

 el argumento izquierdo (es decir, la cadena larga)

(... )⎕R PCRE R eplace las siguientes pautas con estas palabras:

⎕D los dígitos del 0 al 9

 tratar cada uno como un patrón separado

⍺← asignar esta función de reemplazo a (para una alfabetización)

luego,

⊃⍵ el primer personaje del partido

, como una cuerda

 aplicar a ella

w← asignar esto a w(por palabra )

' '∊... : si el espacio es un miembro del mismo (es decir, si la coincidencia estaba vacía):

 no devuelve nada (se convierte en la cadena vacía)

 más,

1=≢⍵: si uno es igual al recuento de caracteres en el partido (es decir, su longitud):

⍺⍵ alfabetizar ese dígito

 más,

3=≢⍵: si tres es igual al recuento de caracteres en el partido (es decir, su longitud):

'Triple',w prepend "Triple" al w ord

 más,

2↓⍵ caer a dígitos del partido

 recurse en eso

w, anteponer la palabra

'Double', anteponer "Doble"

Adán
fuente
6

JavaScript (ES6),  161160152144  bytes

La salida incluye un solo espacio inicial.

s=>[n=>' '+'oh/one/two/three/four/five/six/seven/eight/nine'.split`/`[n],'$1 double$2','triple$2'].map(r=>s=s.replace(/(\S*)( \S+)\2|\d/g,r))&&s

Pruébalo en línea!

o Ver el código fuente formateado

¿Cómo?

La conversión se procesa en tres pasos:

  1. reemplace cada dígito con la palabra en inglés correspondiente, precedida por un espacio
  2. reemplazar cada patrón "X X"con"double X"
  3. reemplazar cada patrón "double X X"con"triple X"

Para guardar bytes, usamos la misma expresión regular para todos los pasos:

/(\S*)( \S+)\2|\d/g

que funciona de la siguiente manera:

(\S*)  -> 1st capturing group: any word, or nothing at all
( \S+) -> 2nd capturing group: a space, followed by a word
\2     -> a copy of the 2nd capturing group
|\d    -> or try to capture a digit instead (for step 1)

En el paso 1, usamos una función de devolución de llamada que selecciona la palabra correcta de una tabla de búsqueda:

  • "799999"" seven nine nine nine nine nine"

En el paso 2, reemplazamos con "$1 double$2":

  • " (seven)( nine)( nine)"" seven double nine"
  • "( nine)( nine) nine"" double nine nine"

En el paso 3, reemplazamos con "triple$2":

  • " (double)( nine)( nine)"" triple nine"
Arnauld
fuente
4

Stax , 56 bytes

ÇÖ◘⌡¿╒ô╞Γ▓8m☻t7♦3├(Ä⌂≤(┬Ω☻9▲ç╕C╞⌡òσ╗─╣╥─☻╪▼⌡5■ÿ,(┬¥?☺÷•±

Ejecutar y depurarlo

recursivo
fuente
3

Python 2 , 171 169 168 bytes

s=input()
while s:c=s[0];n=(s[1:2]==c)+(s[:3]==c*3!=s[1:4]);print'   eellpbiurotd'[-n:0:-2]+'oh one two three four five six seven eight nine'.split()[int(c)],;s=s[1+n:]

Pruébalo en línea!

-1 byte, gracias a Jitse

TFeld
fuente
Beat me again! Save 1 byte like so
Jitse
@Jitse, that doesn't work for 1312 ;)
TFeld
Ah, you're right!
Jitse
How about this one then: ['','double ','triple '][n] to ' eellpbiurotd'[-n:0:-2] for 168 bytes: Try it online!
Jitse
Alternatively, also 168 bytes
Jitse
3

Perl 5 -p, 111 bytes

s/(\d)\1/ double$1/g;s/\w+(\d)\1/triple$1/g;s/\d/' '.qw(oh one two three four five six seven eigth nine)[$&]/ge

Try it online!

Explanation:

s/(\d)\1/ double$1/g; # Replace non-overlapping double digits with " double<digit>"
s/\w+(\d)\1/triple$1/g; # Replace remaining double digits preceded by "double" with "triple<digit>"
s/\d/' '.qw(oh one two three four five six seven eigth nine)[$&]/ge # Replace digits with " <word>"
wastl
fuente
1
Shaved a few bytes off: 106
Xcali
3

Scala, 213 bytes

Got it. Somehow the recursive version I was trying to build was strongly more verbose-y than this one (still recursive though, but only in one case). Function f takes as an input string the phone number and outputs its phonetics with a trailing whitespace.

var u="oh one two three four five six seven eight nine" split " "
"(.)\\1*".r.replaceAllIn(s,x=>{var o=x.matched
var k=u(o(0)-48)+" "
o.length match{case 3=>"triple "+k
case 1=>k
case _=>"double "+k+f(o drop 2)}})

Try it online!
Edit : -8b thanks to DrY Wit!

Scala, 215 bytes

And here comes the leading whitespace version, two bytes longer for some reason (even with massive refactoring).

var u="oh one two three four five six seven eight nine" split " "
"(.)\\1*".r.replaceAllIn(s,x=>{var o=x.matched
var k=u(o(0)-48)
" , double , triple ".split(",")(if(o.length>3){k+=f(o drop 2);1}else o.length-1)+k})

Try it online!

V. Courtois
fuente
2
You can save 8 bytes by replacing (o(0)+"").toInt with o(0)-48.
Dr Y Wit
Oof well done @DrYWit thanks!
V. Courtois
3

PHP, 174 169 166 159 bytes

for(;$s=strspn($argn,$d=$argn[$i],$i++);$s==3?($i+=2)+print'triple ':$s<2?:++$i+print'double ',print[oh,one,two,three,four,five,six,seven,eight,nine][$d].' ');

Try it online!

For each digit at index of $i starting from 0:

  • If span of the same digit starting from location of $i is equal to 3, prints 'triple ' and adds 2 to $i so next iteration will have 2 digits jumped over.
  • If span of the same digit starting from location of $i is equal to or more than 2 but is not 3, prints 'double ' and adds 1 to $i so next iteration will have 1 digit jumped over.
  • Prints word for the digit and a space.
  • $i++.
Night2
fuente
2

Retina 0.8.2, 105 bytes

+`(.)\1
=$1
.
 $&
= =
triple
=
double
9
nine
8
eight
7
seven
6
six
5
five
4
four
3
three
2
two
1
one
0
oh

Try it online! Outputs a leading space. Explanation: I originally tried a regex that automatically matches 2 or 3 digits but @Arnauld's approach of turned out to be golfier. Explanation:

+`(.)\1
=$1

Match pairs of identical digits and replace the first with a =. Then repeat, so that for an odd number the second last digit is also replaced with a =.

.
 $&

Space the digits (and =s) out.

= =
triple

Handle the case of three identical digits.

=
double
9
nine
8
eight
7
seven
6
six
5
five
4
four
3
three
2
two
1
one
0
oh

Replace all remaining characters with words.

Neil
fuente
2

Jelly, 59 bytes

⁵;`,0;$Ɗ€Ẏ;`Ø0ṭ;-œṣjƭƒV€‘$ị“¡ıc⁴Ṛ⁽]@ɱ2¦Ż©Ẉḷ$Æ!)ɗ[ı8ɱḃ%ċ»Ḳ¤K

Try it online!

A monadic link that takes a string of digit characters as its argument and returns a Jelly string of space-separated words. When called as a full-program, outputs implicitly.

Nick Kennedy
fuente
2

T-SQL 2017, 238 bytes

Added some linebreaks to make it readable

WHILE''<left(@,1)SELECT @=stuff(@,1,iif(p<4,p,2),'')+
iif(p=1,' ',iif(p=3,' triple ',' double '))
+trim(substring('oh  one  two  threefour five six  seveneightnine',left(@,1)*5,5))
FROM(SELECT~-patindex('%[^'+left(@,1)+']%'+'^',@)p)z
PRINT @

Try it online

t-clausen.dk
fuente
2

C++, 382 bytes

It's not superclever, but someone needed to write a C++ version. The recursive function R goes through the input string and counts repeated values. If there are more than 3 repeats, it pretends there were 2 repeats, then rewinds and tries again.

A few more source chars could probably be squeezed out with #define major, but I'm sure a better algo could squeeze out more.

#include <iostream>
#include <sstream>
using namespace std;
char*n[]={"oh","one","two","three","four","five","six","seven","eight","nine"};
void R(ostream& s,const char*r,char p='x',int c=0){if(*r==p)R(s,r+1,p,c+1);else
{if(c>1){if(c>= 4){s<<"double ";r-=(c-2);}else if(c==3)s<< "triple ";else if(c==2)s<< "double ";
}if(c >0)s<<n[p-'0']<<" ";if(!*r)return;R(s,r+1,*r,1);}}

void check(const char* in, const char* out)
{
    std::stringstream ss;
    R(ss,in);
    if (out == ss.str()) std::cout << "PASS: "; else std::cout << "FAIL! ";
    std::cout << in << "\n< " << out << "\n> " << ss.str() << std::endl;
}

int main(int c,char**argv)
{
    if (argv[1] == std::string("test"))
    {
        check("0123"         ,"oh one two three ");
        check("4554554"      ,"four double five four double five four ");
        check("000"          ,"triple oh ");
        check("00000"        ,"double oh triple oh ");
        check("66667888"     ,"double six double six seven triple eight ");
        check("19999999179"  ,"one double nine double nine triple nine one seven nine ");
    }
    else
    {
        char* v = argv[1];
        R(std::cout,v);
        std::cout << std::endl;
    }
}

and verification of test cases:

pa-dev01$ ./a.out test
PASS: 0123
< oh one two three
> oh one two three
PASS: 4554554
< four double five four double five four
> four double five four double five four
PASS: 000
< triple oh
> triple oh
PASS: 00000
< double oh triple oh
> double oh triple oh
PASS: 66667888
< double six double six seven triple eight
> double six double six seven triple eight
PASS: 19999999179
< one double nine double nine triple nine one seven nine
> one double nine double nine triple nine one seven nine
Mark Lakata
fuente
1
Does the golfed part actually need #include <sstream>? Or could you move that down after the golfed part for the test function? I think typing std::ostream&s would take less space than using namespace std;, unless there are other places where you'd need a std::.
Peter Cordes
253 bytes
ceilingcat
2

Perl 6, 96 93 bytes

{S:g/(.)$0?[$0{}<!$0>]?/{(<triple double>X$)[3-$/.comb]}{+$/??uniname(~$0).words[1]!!'oh'} /}

Try it online!

This is an anonymous code block that takes a number and returns a string with the numbers in uppercase, e.g. 0123 => oh ONE TWO THREE with a single trailing space.

This was deleted for a while until I found out how to use captures in a lookahead, but it should be fixed now.

Jo King
fuente
1

Red, 242 bytes

func[s][b:[copy t skip t]parse s[any[change[b t ahead not t](rejoin["triple "t])|
change b(rejoin["double "t])| skip]]foreach c s[prin either i:
find"0123456789"c[rejoin[pick[:oh:one:two:three:four:five:six:seven:eight:nine]index? i" "]][c]]]

Try it online!

Galen Ivanov
fuente
1

Scala, 253 bytes

def g(s:String):String={val t="oh one two three four five six seven eight nine".split(" ")(s(0)-48)
s.length match{case 3=>"triple "+t;case 2=>"double "+t;case 1=>t;case _=>"double "+t+" "+g(s drop 2)}}
s=>"(.)\\1*".r.findAllIn(s).map(g(_)) mkString " "

Try it online!

Dr Y Wit
fuente
1

Oracle SQL, 578 bytes (in formatted form)

Solution is not concise by any means so posting it in formatted way.

with r(s) as
(select x from t
  union all
 select case
           when length(regexp_substr(s, '(.)(\1)+')) = 3 
           then regexp_replace(s, '^...')
           else regexp_replace(s, '^(.)\1|^.')
        end
   from r
  where s is not null)
select listagg(decode(length(r),  2, 'double ',  3, 'triple ') ||
               decode(substr(r, 1, 1), 0, 'oh', to_char(to_date(substr(r, 1, 1), 'j'), 'jsp')), ' ')
               within group (order by rownum)
  from (select regexp_replace(s, lag(s) over (order by length(s)) || '$') r
          from r order by length(s) desc);

Test in SQL*Plus

SQL> create table t(x) as select /*'45547777777774'*/ '1999999910079' from dual;

Table created.

SQL> set pages 0
SQL> with r(s) as
  2  (select x from t
  3    union all
  4   select case
  5             when length(regexp_substr(s, '(.)(\1)+')) = 3
  6             then regexp_replace(s, '^...')
  7             else regexp_replace(s, '^(.)\1|^.')
  8          end
  9     from r
 10    where s is not null)
 11  select listagg(decode(length(r),  2, 'double ',  3, 'triple ') ||
 12                 decode(substr(r, 1, 1), 0, 'oh', to_char(to_date(substr(r, 1, 1), 'j'), 'jsp')), ' ')
 13                 within group (order by rownum)
 14    from (select regexp_replace(s, lag(s) over (order by length(s)) || '$') r
 15            from r order by length(s) desc);
one double nine double nine triple nine one double oh seven nine

The main trick is that digits converted into words using Oracle format models instead of hard-coded literals "one" ... "nine".

Dr Y Wit
fuente
any chance of this getting golfed ? it seems you can remove a bunch of spaces. I also imagine you can rewrite WHERE s is not null to WHERE s>''
t-clausen.dk
1
You can save a few characters by replacing what’s after union all with select regexp_replace(s,case when length(regexp_substr(s, '(.)(\1)+')) = 3 then '^...' else '^(.)\1|^.' end) from r.
Steve Kass
1

JavaScript, 142 bytes

s=>s.replace(/(.)(\1\1(?!\1)|\1|)/g,t=>(t[2]?' triple ':t[1]?' double ':' ')+'oh one two three four five six seven eight nine'.split` `[t[0]])

Try it online!

tsh
fuente
1

(Roblox) Lua 5.1, 166 bytes

for I,N in('111 triple 11 double 1 '):gmatch'(%d+)(%D+)'do for i,n in('0oh1one2two3three4four5five6six7seven8eight9nine'):gmatch'(.)(%l+)'do s=s:gsub(i*I,N..n)end end

Ensure s is a predefined string value populated only with digits; that will be the variable to be modified. The result will include a leading space [\u20] character.

VisualPlugin Rōblox
fuente
Welcome to the site! As Lua can take input via standard methods, it's against the rules to require s to already have the input. Aside from that, you've got a good first post! I would recommend you include a link to an online testing site e.g. tio.run/#lua so that others can test your solution
caird coinheringaahing
Hi. The variant of Lua I was testing on (Rbx.Lua) doesn't contain input methods, although the sandbox has print, warn, and error output methods.
VisualPlugin Rōblox