Как получить набор всех букв в Java / Clojure? - PullRequest
11 голосов
/ 05 апреля 2010

В Python я могу сделать это:

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

Есть ли какой-нибудь способ сделать что-то подобное в Clojure (кроме копирования и вставки указанных выше символов где-нибудь)? Я просмотрел как стандартную библиотеку Clojure, так и стандартную библиотеку java и не смог ее найти.

Ответы [ 8 ]

20 голосов
/ 05 апреля 2010

Если вы просто хотите использовать символы Ascii,

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

даст,

(\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)
13 голосов
/ 05 апреля 2010

Правильно не ASCII-ориентированная реализация:

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();
}

Назовите это с помощью US-ASCII, и вы получите желаемый результат (за исключением того, что заглавные буквы идут первыми). Вы могли бы назвать это с Charset.defaultCharset(), но я подозреваю, что вы получите гораздо больше, чем буквы ASCII на большинстве систем, даже в США.

Предостережение: рассматривает только основной многоязычный план. Не будет слишком сложно распространиться на дополнительные плоскости, но это займет намного больше времени, а полезность сомнительна.

6 голосов
/ 05 апреля 2010

На основе императивного Java-решения Майклса это идиоматическое (ленивые последовательности) решение 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))))

Обновление: Спасибо Cgrand за это предпочтительное решение высокого уровня:

(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 %))))))

Но сравнение производительности моего первого подхода

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)  

и ваше решение

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) 

довольно интересно.

5 голосов
/ 05 апреля 2010

Нет, потому что это просто распечатка букв ASCII, а не полный набор. Конечно, легко распечатать 26 строчных и прописных букв, используя два цикла for, но факт заключается в том, что за пределами первых 127 кодовых точек гораздо больше «букв». «IsLetter» Java на Character будет верным для этих и многих других.

3 голосов
/ 05 апреля 2010

string.letters: Описана конкатенация строк в нижнем и верхнем регистре ниже. Конкретное значение зависит от локали и будет обновляться когда вызывается locale.setlocale ().

Я изменил ответ от Майкла Боргвардта. В моей реализации есть два списка lowerCases и upperCases по двум причинам:

  1. string.letters - строчные буквы, за которыми следуют прописные буквы.

  2. Java Character.isLetter (char) - это больше, чем просто верхний и нижний регистры, поэтому использование Character.isLetter (char) вернет много результатов в некоторых наборах символов, например, "windows- 1252"

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

Персонаж считается письмо, если его общий тип категории, предоставляется Character.getType (ch), является любой из следующих:

* UPPERCASE_LETTER
* LOWERCASE_LETTER
* TITLECASE_LETTER
* MODIFIER_LETTER
* OTHER_LETTER 

Не все буквы имеют регистр. Много символы являются буквами, но не являются Прописные, ни строчные, ни заглавные.

Так что если string.letters должен возвращать только строчные и прописные буквы, TITLECASE_LETTER, , Символы MODIFIER_LETTER и OTHER_LETTER должны игнорироваться.

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();
}

Дополнительно: поведение string.letters изменяется при изменении локали. Возможно, это не относится к моему решению, потому что изменение языка по умолчанию не меняет кодировку по умолчанию. Из apiDoc:

Определена кодировка по умолчанию во время запуска виртуальной машины и как правило, зависит от региона и кодировка основной операционной система.

Полагаю, кодировка по умолчанию не может быть изменена в запущенной JVM. Таким образом, поведение «изменить язык» для string.letters не может быть реализовано только с помощью Locale.setDefault (Locale). Но в любом случае изменение языка по умолчанию - плохая идея:

Поскольку изменение локали по умолчанию может влияет на множество различных областей функциональность, этот метод должен только использоваться, если вызывающий абонент готов к переинициализировать код, чувствительный к локали работает в том же Java Virtual Машина.

1 голос
/ 07 апреля 2010

Если вы не помните диапазоны кодовых точек. Перебор грубой силы :-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> 
1 голос
/ 05 апреля 2010

Тот же результат, который упоминался в вашем вопросе, будет дан следующим утверждением, которое должно быть сгенерировано вручную в отличие от решения 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());
    }

}
1 голос
/ 05 апреля 2010

Я почти уверен, что буквы не доступны в стандартной библиотеке, поэтому вы, вероятно, останетесь с ручным подходом.

...