2012-07-06 11 views

risposta

15

Prova questo:

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 per uso produttivo dell'affermazione elusiva di look-behind negativa a larghezza zero. Sebbene (purtroppo) come fa notare Bill Ruppert, esiste già un modulo CPAN. –

+3

Anche se ci * è * una soluzione CPAN, mi piace anche questa. È ben congegnato, altamente leggibile, privo di dipendenze e preciso come la soluzione CPAN per qualsiasi intero. – DavidO

27

Usa Lingua::EN::Numbers::Ordinate. Dalla sinossi:

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

Prova questa breve subroutine

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"; 
} 

uscita

42nd 
521st 
113th 
+0

C'è qualcosa che non capisco completamente qui. Perché un ciclo 'for' quando c'è un solo elemento come argomento? Potrebbe anche funzionare 'return int (shift). (qw/... '. Per molti parametri il ciclo' for' non funzionerebbe neanche a causa dell'istruzione 'return'. Funziona bene così com'è, ma mi sono perso qualcosa del ciclo? – Birei

+0

@Birei: è solo un modo di mettere '$ _ [0]' in '$ _'. Il tuo modo non funzionerebbe in quanto l'espressione regolare ha bisogno che il valore sia in' $ _'. È molto simile alla nuova parola di lingua 'given' ma tu non posso usare questo come un modificatore di istruzioni come è possibile con 'for'. – Borodin

+0

Ah, ok Grazie, non ho capito il punto di $ _ _. Merita un ** + 1 **. – Birei

2

Ecco una soluzione which I originally wrote for a code golf challenge, leggermente riscritto per conformarsi alle consuete buone pratiche per il codice non-golf :

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

Il modo in cui funziona è che l'espressione regolare (1?\d)$ corrisponde all'ultima cifra del numero, più la cifra precedente se è 1. La sostituzione utilizza quindi la (e) cifra (e) corrispondente come indice alla lista (qw'th st nd rd'), mappando da 0 a , da 1 a , da 3 a rd e qualsiasi altro valore a undef. Infine, l'operatore || sostituisce undef con th.

Se non ti piace s///e, in sostanza la soluzione potrebbe essere scritta ad es. in questo modo:

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

o come una funzione:

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

Un'altra soluzione (se mi piace le risposte preesistenti che sono indipendenti di utilizzare moduli migliore):

use Date::Calc 'English_Ordinal'; 
print English_Ordinal $ARGV[0]; 
Problemi correlati