2012-07-06 7 views

Respuesta

15

Prueba esto:

my $ordinal; 
if ($foo =~ /(?<!1)1$/) { 
    $ordinal = 'st'; 
} elsif ($foo =~ /(?<!1)2$/) { 
    $ordinal = 'nd'; 
} elsif ($foo =~ /(?<!1)3$/) { 
    $ordinal = 'rd'; 
} else { 
    $ordinal = 'th'; 
} 
+2

Upvote para el uso productivo de la escurridiza aserción de mirada negativa de ancho cero. Aunque (tristemente) como señala Bill Ruppert, ya hay un módulo de CPAN para esto. –

+3

Aunque hay * una * solución de CPAN, me gusta esta también. Está bien pensado, es muy legible, está desprovisto de dependencias y es tan preciso como la solución CPAN para cualquier número entero. – DavidO

27

Uso Lingua::EN::Numbers::Ordinate. A partir de la sinopsis:

use Lingua::EN::Numbers::Ordinate; 
print ordinate(4), "\n"; 
# prints 4th 
print ordinate(-342), "\n"; 
# prints -342nd 

# Example of actual use: 
... 
for(my $i = 0; $i < @records; $i++) { 
    unless(is_valid($record[$i]) { 
    warn "The ", ordinate($i), " record is invalid!\n"; 
    next; 
    } 
    ... 
} 
7

probar este breve subrutina

use strict; 
use warnings; 

sub ordinal { 
    return $_.(qw/th st nd rd/)[/(?<!1)([123])$/ ? $1 : 0] for int shift; 
} 

for (42, 521, 113) { 
    print ordinal($_), "\n"; 
} 

salida

42nd 
521st 
113th 
+0

Hay algo que no entiendo completamente aquí. ¿Por qué un bucle 'for' cuando solo hay un elemento como argumento? También podría funcionar 'return int (shift). (qw/... '. Para varios parámetros, el bucle' for' no funcionaría ni a causa de la declaración 'return'. Funciona bien tal como está, pero ¿extrañé algo sobre el bucle? – Birei

+0

@Birei: es solo una forma de poner '$ _ [0]' en '$ _'. Tu camino no funcionaría ya que la expresión regular necesita el valor para estar en' $ _'. Es muy similar a la nueva palabra de lenguaje 'given' pero tú no se puede utilizar como un modificador de declaración como se puede con 'for'. – Borodin

+0

Ah, vale. Gracias. No entendí el punto de' $ _'. Merece un ** + 1 **. – Birei

2

he aquí una solución which I originally wrote for a code golf challenge, ligeramente reescrito para ajustarse a las mejores prácticas habituales de código no-golf :

$number =~ s/(1?\d)$/$1 . ((qw'th st nd rd')[$1] || 'th')/e; 

La forma en que funciona es que la expresión regular (1?\d)$ coincide con el último dígito del número, más el dígito anterior si es 1. La sustitución utiliza los dígitos coincidentes como índice de la lista (qw'th st nd rd'), asignando 0 a th, 1 a st, 2 a nd, 3 a rd y cualquier otro valor a undef. Finalmente, el operador || reemplaza undef por th.

Si no te gusta s///e, básicamente se puede escribir la misma solución, p. de esta manera:

for ($number) { 
    /(1?\d)$/ or next; 
    $_ .= (qw'th st nd rd')[$1] || 'th'; 
} 

o como una función:

sub ordinal ($) { 
    $_[0] =~ /(1?\d)$/ or return; 
    return $_[0] . ((qw'th st nd rd')[$1] || 'th'); 
} 
0

Otra solución (aunque me gusta las respuestas preexistentes que son independientes de la utilización de módulos mejores):

use Date::Calc 'English_Ordinal'; 
print English_Ordinal $ARGV[0]; 
Cuestiones relacionadas