Haskell, Char, Unicode, and Turkish

27

对于 Char 数据类型,如何指定在 toLower 和 toUpper 函数中使用土耳其语的 "i" 而不是英语的 "i"?


8
收藏。土耳其语因无点的"I"问题而棘手。这是测试此类功能的最佳案例。 - Alexandre C.
13
相对于正确处理Unicode,理解单子是小菜一碟。 - C. A. McCann
4
@Alex: 土耳其语?更不用提德语的多字母大写 ß -> SS 以及希腊语中根据上下文变化的小写 Σ -> σ/ς。 - kennytm
1
土耳其语会破坏Unicode的ASCII部分,因此是一个很好的测试案例。 - Alexandre C.
我认为德国的情况是不公平的。人们不会期望一个返回字符的函数能够返回一对字符。 - Jonathan Allen
1
@Jonathan Allen:按照这个论点,Unicode 字符的 toUpper 函数必须要么是不正确的,要么返回一个字符串。两者都不是很吸引人。 - C. A. McCann
3个回答

17

text和text-icu包

截至2011年,最好使用text包以及Text ICU包中的toLower函数,该函数支持由语言环境参数化的Char操作。

来自这个示例:

import Data.Text (pack, unpack)
import Data.Text.ICU (LocaleName(Locale), toLower)

main = do
  let trLocale = Locale "tr-TR"
      upStr    = "ÇIİĞÖŞÜ"
      lowStr   = unpack $ toLower trLocale $ pack upStr
  putStrLn $ "toLower " ++ upStr ++ " gives " ++ lowStr

运行以下代码:

> toLower ÇIİĞÖŞÜ gives çıiğöşü

虽然这个例子是将 String 之间进行转换,但你也可以直接将数据保留在 text 格式中。


16
在 Haskell 中,Data.Char 库不依赖于本地设置。它适用于所有 Unicode 字符,但可能并不像您期望的那样工作。在相应的 Unicode 图表中,您可以看到“点状”/“无点”的 i 的映射。
  • toUpper 'i' => 'I'
  • toUpper 'ı' => 'I'
  • toLower 'I' => 'i'
  • toLower 'İ' => 'i'
因此,很明显这两个转换都是不可逆的。如果您想要对土耳其字符进行可逆处理,似乎必须使用 C 库或自己编写。 更新: Haskell 98 报告已经非常清楚说明了这一点,而Haskell 2010 报告只是说Char对应于一个 Unicode 字符,并没有明确定义toLowertoUpper的语义。

toLower 'I' 应返回无点的 i - Alexandre C.
3
@Alexandre: 我记录了Haskell的工作原理以及(链接的) Unicode 规范所述内容。如果你想要其他行为,你需要自己实现它(就像 jrockway 的回复中所说的那样)。 - grddev

8

一个简单的编程问题:

import qualified Data.Char as Char

toLower 'I' = 'ı'
toLower x   = Char.toLower x

那么

toLower <$> "I AM LOWERCASE" == "ı am lowercase"  

你真的是在告诉我,为了支持国际化,我必须要黑掉每个调用Char.toLower函数的库吗? - Jonathan Allen
5
@Jonathan: 是的,因为Haskell规范只要求遵循Unicode标准,而该标准提供了我上面所给出的规则。因此,任何使用Char.toLower函数的库都没有为国际化做好准备。 - grddev
2
@Jonathan Allen:如果您不想使用标准的Unicode行为,那么您不能使用遵循Unicode标准的库。这很不幸,但很明显如此。 - Chuck
4
我应该澄清一下,这并不是最佳解决方案。编写一个比 Data.Char 更灵活的库会更好,并且社区肯定会欣赏在这方面做出的任何贡献。 - jrockway

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接