show w.r.t.的语义转义字符

时间:2018-02-17 22:12:23

标签: haskell ghc

考虑以下示例(λ> = ghci,$ = shell):

λ> writeFile "d" $ show "d"
$ cat d
"d"

λ> writeFile "d" "d"
$ cat d
d

λ> writeFile "backslash" $ show "\\"
$ cat backslash
"\\"

λ> writeFile "backslash" "\\"
$ cat backslash
\

λ> writeFile "cat" $ show "" -- U+1F408
$ cat cat
"\128008"

λ> writeFile "cat" ""
$ cat cat

我理解"\128008"的另一种方式只是另一种表达方式 Haskell源代码中的""。 我的问题是:为什么""示例的行为类似于反斜杠而不是 像"d"?因为它是一个可打印的角色,所以不应该表现得像 一封信?

更一般地说,确定角色是否适用的规则是什么 显示为可打印字符还是转义码?我看了Section 6.3 在Haskell 2010语言报告中,但它没有指定确切的行为。

1 个答案:

答案 0 :(得分:6)

TL:DR; ASCII范围(0-127)内的可打印字符将为show n作为图形字符。*其他所有内容都将被转义。

*除了双引号(因为它们用于字符串分隔符)和 反斜杠(因为它们是逃逸所必需的)。

让我们看看源代码来解决这个问题!

由于我们有String = [Char],我们应该寻找instance Show Char 来源。它可以找到 here。 它被定义为:

-- | @since 2.01
instance  Show Char  where
    showsPrec _ '\'' = showString "'\\''"
    showsPrec _ c    = showChar '\'' . showLitChar c . showChar '\''

    showList cs = showChar '"' . showLitString cs . showChar '"'

因此显示String(使用showList)基本上是一个包装器 ShowLitString,并显示CharShowLitChar的包装。 让我们来看看这些功能。

showLitString :: String -> ShowS
-- | Same as 'showLitChar', but for strings
-- It converts the string to a string using Haskell escape conventions
-- for non-printable characters. Does not add double-quotes around the
-- whole thing; the caller should do that.
-- The main difference from showLitChar (apart from the fact that the
-- argument is a string not a list) is that we must escape double-quotes
showLitString []         s = s
showLitString ('"' : cs) s = showString "\\\"" (showLitString cs s)
showLitString (c   : cs) s = showLitChar c (showLitString cs s)
   -- [explanatory comments ...]

正如您所期望的那样,showLitString主要是一个包装器 showLitChar。 [注意:如果您不熟悉ShowS类型,这很好 answer了解原因 它可能有用。] 不是我们想要的,所以让我们去showLitChar(我已经 省略了与问题无关的定义部分。

-- | Convert a character to a string using only printable characters,
-- using Haskell source-language escape conventions.  For example:
-- [...]
showLitChar                :: Char -> ShowS
showLitChar c s | c > '\DEL' =  showChar '\\' (protectEsc isDec (shows (ord c)) s)
-- ^ Pattern matched for cat
showLitChar '\DEL'         s =  showString "\\DEL" s
showLitChar '\\'           s =  showString "\\\\" s
-- ^ Pattern matched for backslash
showLitChar c s | c >= ' '   =  showChar c s
-- ^ Pattern matched for d
-- Some more escape codes
showLitChar '\a'           s =  showString "\\a" s
-- similarly for '\b', '\f', '\n', '\r', '\t', '\v' etc.
-- showLitChar ... = ...

现在你看到问题所在。 ord cint,第一个是ord '\DEL' == 127 对于所有非ASCII字符(-- | @since 2.01 instance Show Char where )。 对于ASCII范围内的字符,将打印可打印字符 其余的都逃脱了。对于外面的字符,所有字符都被转义。

代码没有回答问题的“原因”部分。答案就是这样 (我认为)是我们看到的第一条评论:

git blame

如果我猜测,这种行为一直存在以保持倒退 兼容性。我不需要猜测:请参阅评论以获得一些好的答案。

加成

我们可以使用GHC的Github镜像在线进行Data.Char;)。让我们来看看 这段代码写的时候 (blame link)。 相关的commit是15岁(!)。但是,确实提到了Unicode。

区分不同类型Unicode字符的功能 存在于isPrint c = iswprint (ord c) /= 0 foreign import ccall unsafe "u_iswprint" iswprint :: Int -> Int 模块中。查看source

iswprint

如果你跟踪引入void *exec_threads(void* arg){ int *counter = (int*)arg; int index = mult/threads * (*counter); SortedListElement_t* element = (SortedList_t*)malloc(sizeof(SortedList_t)); for(int i = 0; i < iterations; i++){ element->key = &keys[index + i]; SortedList_insert(list, element); } for(int i = 0; i < iterations; i++){ element = SortedList_lookup(list, &keys[index + i]); if(element == NULL){ fprintf(stderr, "Could not find element %c in the list\n", keys[index+i]); exit(2); } if(SortedList_delete(element)){ fprintf(stderr, "Corrupted list\n"); exit(2); } } free(arg); return NULL; 的提交,你就会登陆 here。这项承诺是在13年前作出的。 也许这两年他们没有写出足够的代码 想打破?我不知道。如果一些GHC开发人员可以更多地了解这一点, 在评论中,Daniel Wagner和Paul Johnson指出了一个很好的理由 - 使用非Unicode系统进行操作必须是一个高优先级(大约15年前),因为它非常棒。当时Unicode是相对较新的。