2012-07-06 7 views

Antwort

15

Versuchen Sie folgendes:

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 für den produktiven Einsatz der schwer zu fassenden Negativ-Look-Behind-Assertion. Obwohl (leider) Bill Ruppert darauf hinweist, gibt es dafür bereits ein CPAN-Modul. –

+3

Obwohl es eine CPAN-Lösung gibt, gefällt mir auch diese. Es ist gut durchdacht, gut lesbar, ohne Abhängigkeiten und so genau wie die CPAN-Lösung für jede ganze Zahl. – DavidO

27

Verwendung Lingua::EN::Numbers::Ordinate. Aus der Übersicht:

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

Versuchen Sie, diese kurze Unterprogramm

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

Ausgang

42nd 
521st 
113th 
+0

Es gibt etwas, das ich hier nicht vollständig verstehe. Warum eine 'for'-Schleife, wenn nur ein Element als Argument existiert? Es könnte auch 'return int (shift) funktionieren. (qw/... '. Für einige Parameter würde die 'for'-Schleife nicht funktionieren, auch nicht wegen der' return'-Anweisung. Es funktioniert gut wie es ist, aber habe ich etwas an der Schleife übersehen? – Birei

+0

@Birei: es ist nur eine Möglichkeit, '$ _ [0]' in '$ _' zu setzen.Ihr Weg würde nicht funktionieren, da der reguläre Ausdruck den Wert in' $ _' benötigt.Es ist sehr wie das neue 'gegebene' Sprachwort, aber Sie kann das nicht als Statement-Modifier verwenden, wie du es mit 'for' kannst." – Borodin

+0

Ah, ok. Danke. Hat nicht den Sinn von '$ _'. Es verdient eine ** + 1 **. – Birei

2

Hier eine Lösung ist which I originally wrote for a code golf challenge, etwas für die Code-nicht-Golf zu üblichen Best Practices entsprechen neu geschrieben :

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

Die Art und Weise, wie es funktioniert, ist, dass die Regexp (1?\d)$ die letzte Ziffer der Nummer übereinstimmt, plus die vorangehende Ziffer, wenn es 1 ist. Die Substitution verwendet dann die übereinstimmenden Ziffern als Index für die Liste (qw'th st nd rd'), Abbildung 0 bis th, 1 bis st, 2 bis nd, 3 bis rd und jeden anderen Wert für undef. Schließlich ersetzt der Operator || undef durch th.

Wenn Sie nicht mögen s///e, könnte im Wesentlichen die gleiche Lösung geschrieben werden, z. dies wie:

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

oder als Funktion:

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

eine andere Lösung (obwohl ich mag die bereits existierenden Antworten, die einer Verwendung der Module besser unabhängig sind):

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