2010-04-05 13 views
11

W Pythonie, mogę to zrobić:Jak uzyskać zestaw wszystkich liter w Java/Clojure?

>>> import string 
>>> string.letters 
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 

Czy istnieje jakiś sposób, aby zrobić coś podobnego w Clojure (oprócz kopiowania i wklejania powyższe znaki gdzieś)? Przejrzałem bibliotekę standardową Clojure i bibliotekę standardową Java i nie mogłem jej znaleźć.

+6

Uważam, że wynik Pythona jest całkowicie niepoprawny. A co z ä, é lub ß? –

+4

Przypuszczam, że byłoby pomocne, jeśli dałem dokumentację na 'string.letters' Pythona: http://docs.python.org/library/string.html#string.litery Jest oparty na ASCII, więc zwrócona wartość zależy od systemu. –

+4

jak "wartość zależy od systemu" wynika z "To jest oparte na ASCII"? ASCII jest dobrze zdefiniowany i jest dokładnie taki sam w każdym systemie. Więc jeśli byłby naprawdę oparty na ASCII, wynik byłby zawsze taki sam. "ASCII" nie jest synonimem "jakiegokolwiek 8-bitowego kodowania znaków". –

Odpowiedz

13

implementacji właściwie non-ASCII-centric:

private static String allLetters(String charsetName) 
{ 
    CharsetEncoder ce = Charset.forName(charsetName).newEncoder(); 
    StringBuilder result = new StringBuilder(); 
    for(char c=0; c<Character.MAX_VALUE; c++) 
    { 
     if(ce.canEncode(c) && Character.isLetter(c)) 
     { 
      result.append(c); 
     } 
    } 
    return result.toString(); 
} 

Zadzwoń do tego z "US-ASCII", a uzyskasz pożądany wynik (oprócz tego, że wielkie litery są na pierwszym miejscu). Można to nazwać z numerem Charset.defaultCharset(), ale podejrzewam, że otrzymasz znacznie więcej niż litery ASCII na większości systemów, nawet w USA.

Zastrzeżenie: uwzględniany jest tylko podstawowy samolot wielojęzyczny. Nie byłby zbyt trudny do rozszerzenia na samoloty uzupełniające, ale trwałoby to znacznie dłużej, a użyteczność jest wątpliwa.

+3

Character.isLetter (char) jest więcej niż uppercases i lowercases: Postać jest uważana za pismo, jeżeli jego ogólny typ kategorii, pod warunkiem przez Character.getType (CH), jest jedną z następujących czynności: * UPPERCASE_LETTER * LOWERCASE_LETTER * TITLECASE_LETTER * MODIFIER_LETTER * OTHER_LETTER Nie wszystkie litery mają wielkość liter. Wiele znaków to litery, ale nie są one dużymi literami ani małymi literami ani tytułami. –

+0

Trochę miła sztuczka: możesz napisać warunek 'for' w ten sposób' dla (char c = 1; c> 0; C++) '. – Elist

1

Jestem prawie pewny, że litery nie są dostępne w standardowej bibliotece, więc prawdopodobnie masz ręczne podejście.

5

Nie, ponieważ jest to po prostu drukowanie liter ASCII zamiast pełnego zestawu. Oczywiście drukowanie 26 małych i wielkich liter za pomocą dwóch dla pętli jest banalne, ale faktem jest, że poza pierwszymi 127 punktami kodowymi istnieje znacznie więcej "liter". Java "isLetter" fn na Character będzie prawdziwa dla tych i wielu innych.

+0

To doskonały punkt, ale teraz nie martwię się zbytnio o Unicode. To powiedziawszy, przypuszczam, że mógłbym po prostu zastosować podejście manualne. To nie tak, że alfabet może wkrótce ulec zmianie. :-) –

+1

@Jason: List "Capital ß" wszedł do standardu Unicode w 2008 roku! I to jest litera z alfabetu łacińskiego! (Przyznane, jest używane bardzo rzadko, ale wciąż: nawet alfabety nie są bezpieczne przed zmianą). –

1

Taki sam wynik jak wspomniano w pytaniu będzie określona następującym stwierdzeniem, że musi być ręcznie wygenerowana w przeciwieństwie do rozwiązania Python:

public class Letters { 

    public static String asString() { 
     StringBuffer buffer = new StringBuffer(); 
     for (char c = 'a'; c <= 'z'; c++) 
      buffer.append(c); 
     for (char c = 'A'; c <= 'Z'; c++) 
      buffer.append(c); 
     return buffer.toString(); 
    } 

    public static void main(String[] args) { 
     System.out.println(Letters.asString()); 
    } 

} 
3

string.letters: złączonych łańcuchów małymi literami wielkimi opisanych poniżej. Konkretna wartość to zależna od ustawień regionalnych i zostanie zaktualizowana po wywołaniu funkcji locale.setlocale().

Ja zmodyfikowałem odpowiedź od Michaela Borgwardta. W moim realizacji istnieją dwa wymienia lowerCases i upperCases z dwóch powodów:

  1. string.letters jest lowercases obserwowani przez uppercases.

  2. Java Character.isLetter(char) jest czymś więcej niż tylko uppercases i lowercases, więc użycie Character.isLetter (char) powróci do dużo wyników w niektórych zestawów znaków, na przykład "windows-1252"

Od Api-Doc: Character.isLetter(char):

postać jest uważana za list, jeżeli jego ogólny typ kategoria, dostarczone przez Character.getType (cH), jest jedną z następujących czynności:

* UPPERCASE_LETTER 
* LOWERCASE_LETTER 
* TITLECASE_LETTER 
* MODIFIER_LETTER 
* OTHER_LETTER 

Nie wszystkie litery mają wielkość liter.Wiele znaków to litery, ale nie są one ani wielkie, ani małe, ani tytułowe.

Więc jeśli string.letters powinien zwrócić tylko lowercases i uppercases The TITLECASE_LETTER, , MODIFIER_LETTER i OTHER_LETTER znaków mają być ignorowane.

public static String allLetters(final Charset charset) { 
    final CharsetEncoder encoder = charset.newEncoder(); 
    final StringBuilder lowerCases = new StringBuilder(); 
    final StringBuilder upperCases = new StringBuilder(); 
    for (char c = 0; c < Character.MAX_VALUE; c++) { 
    if (encoder.canEncode(c)) { 
    if (Character.isUpperCase(c)) { 
    upperCases.append(c); 
    } else if (Character.isLowerCase(c)) { 
    lowerCases.append(c); 
    } 
    } 
    } 
    return lowerCases.append(upperCases).toString(); 
} 

Dodatkowo: zachowanie string.letters zmian przy zmianie ustawienia regionalne. To może nie dotyczyć mojego rozwiązania, ponieważ zmiana domyślnego ustawienia narodowego nie zmienia domyślnego zestawu znaków. Od apiDoc:

Domyślna charset określa podczas uruchamiania wirtualnych maszyn i zazwyczaj zależy od lokalizacji i charset bazowego systemu operacyjnego .

Domyślny zestaw znaków nie może zostać zmieniony w ramach uruchomionej maszyny JVM. Tak więc zachowanie "zmiany ustawień" string.letters nie może zostać zrealizowane tylko za pomocą Locale.setDefault (Locale). Ale zmienia domyślne ustawienia regionalne to i tak zły pomysł:

Ponieważ zmienia domyślne ustawienia regionalne mogą wpływają na wiele różnych obszarów funkcjonalności, metoda ta powinna być stosowane tylko wtedy, gdy rozmówca jest przygotowany do Reinitialize locale wrażliwych kod działający w obrębie tej samej maszyny wirtualnej Java .

20

Jeśli chcesz tylko Ascii znaki,

(map char (concat (range 65 91) (range 97 123))) 

przyniesie,

(\A \B \C \D \E \F \G \H \I \J \K \L \M \N \O \P \Q \R \S \T \U \V \W \X \Y \Z 
\a \b \c \d \e \f \g \h \i \j \k \l \m \n \o \p \q \r \s \t \u \v \w \x \y \z) 
+2

+1 Nie trzeba zawijać wywołania char w funkcji anonimowej, '(map char (concat (zakres 65 91) (zakres 97 123)))' będzie działać dobrze. – Jonas

6

podstawie Michaels rozwiązania konieczne Java, to jest idiomatyczne (leniwe sekwencje) rozwiązanie Clojure:

(ns stackoverflow 
    (:import (java.nio.charset Charset CharsetEncoder))) 

(defn all-letters [charset] 
    (let [encoder (. (Charset/forName charset) newEncoder)] 
    (letfn [(valid-char? [c] 
      (and (.canEncode encoder (char c)) (Character/isLetter c))) 
     (all-letters-lazy [c] 
        (when (<= c (int Character/MAX_VALUE)) 
       (if (valid-char? c) 
        (lazy-seq 
        (cons (char c) (all-letters-lazy (inc c)))) 
        (recur (inc c)))))] 
     (all-letters-lazy 0)))) 

Aktualizacja: Dzięki cgrand tego korzystnego rozwiązania na wysokim poziomie:

(defn letters [charset-name] 
    (let [ce (-> charset-name java.nio.charset.Charset/forName .newEncoder)] 
    (->> (range 0 (int Character/MAX_VALUE)) (map char) 
     (filter #(and (.canEncode ce %) (Character/isLetter %)))))) 

ale porównanie wprawdzie wydajność między moim pierwszym podejściu

user> (time (doall (stackoverflow/all-letters "ascii"))) 
"Elapsed time: 33.333336 msecs"             
(\A \B \C \D \E \F \G \H \I \J \K \L \M \N \O \P \Q \R \S \T \U \V \W \X \Y \Z \\ 
a \b \c \d \e \f \g \h \i \j \k \l \m \n \o \p \q \r \s \t \u \v \w \x \y \z) 

i rozwiązania

user> (time (doall (stackoverflow/letters "ascii"))) 
"Elapsed time: 666.666654 msecs"             
(\A \B \C \D \E \F \G \H \I \J \K \L \M \N \O \P \Q \R \S \T \U \V \W \X \Y \Z \\ 
a \b \c \d \e \f \g \h \i \j \k \l \m \n \o \p \q \r \s \t \u \v \w \x \y \z) 

jest dość ciekawy .

+1

Idiomatyczne leniwe seq fns rzadko używają lazy-seq: lazy-seq jest niskiego poziomu. Rdzeń twojego kodu jest lepiej napisany jako: (- >> (zakres 0 (int Character/MAX_VALUE)) (map char) (filter # (and (.canEncode ce%) (Character/isLetter%))) patrz http: //gist.github.com/357407. Inna sprawa:. i .. są spuściznami, więc ich nie używajcie – cgrand

+0

Dzięki! Dlaczego "." i ".." są uważane za dziedzictwo? Wszelkie zasoby? –

+1

-> jest lepiej ... ponieważ miksujesz fn i metody (w metodzie notacji), więc .. nie ma zainteresowania, z wyjątkiem zapisywania kropki na wywołanie metody (i ułatwianie ich zauważenia, gdy idziesz na typ-podpowiedź). metoda obj) jest bardziej fikcyjna przez umieszczenie metody w pozycji funkcji Podobnie preferuj Foo. do (nowego Foo). Wypróbuj cukrowane formularze (.foo, Foo. i Foo/BAR), a zobaczysz, że są o wiele przyjemniej w użyciu (i później ułatwi rozkład) – cgrand

1

Jeśli nie pamiętasz zakresów punktów kodowych. Brute force way :-P:

user> (require '[clojure.contrib.str-utils2 :as stru2]) 
nil 
user> (set (stru2/replace (apply str (map char (range 0 256))) #"[^A-Za-z]" "")) 
#{\A \a \B \b \C \c \D \d \E \e \F \f \G \g \H \h \I \i \J \j \K \k \L \l \M \m \N \n \O \o \P \p \Q \q \R \r \S \s \T \t \U \u \V \v \W \w \X \x \Y \y \Z \z} 
user> 
Powiązane problemy