Как получить набор всех букв в Java/Clojure?

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

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

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

Ответ 1

Правильная реализация, не ориентированная на 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 на большинстве систем, даже в США.

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

Ответ 2

Если вам просто нужны символы 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)

Ответ 3

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

Update: Спасибо 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) 

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

Ответ 4

Нет, потому что это просто печатает буквы ASCII, а не полный набор. Конечно, тривиально печатать 26 строчных букв и букв верхнего регистра, используя два для петель, но факт в том, что существует гораздо больше "букв" за пределами первых 127 кодовых точек. Java "isLetter" fn on Character будет верным для этих и многих других.

Ответ 5

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

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

  • string.letters - это нижние регионы, за которыми следуют верхние регистры.

  • 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. Таким образом, поведение "change locale" string.letters не может быть реализовано с помощью только Locale.setDefault(Locale). Но изменение языкового стандарта по умолчанию - это плохая идея:

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

Ответ 6

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

Ответ 7

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

}

Ответ 8

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