在 Python 中,我可以这样做:
>>> import string
>>> string.letters
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
有没有办法在 Clojure 中做类似的事情(除了在某处复制和粘贴上述字符)?我浏览了 Clojure 标准库和 java 标准库,都找不到。
如果你只想要 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)
一个正确的非以 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 字母。
警告:只考虑基本的多语言平面。扩展到辅助位面不会太难,但是会花费更长的时间,而且实用性值得怀疑。
基于 Michaels 命令式 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)
很有趣。
不,因为那只是打印出 ASCII 字母而不是完整的集合。当然,使用两个 for 循环打印出 26 个小写和大写字母很简单,但事实是在前 127 个代码点之外还有更多“字母”。Java 对 Character 的“isLetter”fn 将适用于这些和许多其他人。
string.letters:字符串小写和大写的连接,如下所述。具体值取决于语言环境,将在调用 locale.setlocale() 时更新。
我修改了 Michael Borgwardt 的答案。在我的实现中,有两个列表 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 中更改。因此,仅使用 Locale.setDefault(Locale) 无法实现 string.letters 的“更改语言环境”行为。但是更改默认语言环境无论如何都是一个坏主意:
由于更改默认语言环境可能会影响许多不同的功能区域,因此只有在调用者准备重新初始化在同一 Java 虚拟机中运行的对语言环境敏感的代码时才应使用此方法。
我很确定这些字母在标准库中不可用,因此您可能只能使用手动方法。
以下语句将给出与您的问题中提到的相同的结果,与 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());
}
}
如果您不记得代码点范围。蛮力方式:-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>