29

問題を見てください: 通常、インタラクティブな Haskell 環境では、(結果の一部を構成する) 非ラテン Unicode 文字はエスケープされて出力されますputStrLnputChar読み取り可能) -- 例は GHCi と Hugs98 を示しています:

$ ghci
GHCi, version 7.0.1: http://www.haskell.org/ghc/  :? for help
Prelude> "hello: привет"
"hello: \1087\1088\1080\1074\1077\1090"
Prelude> 'Я'
'\1071'
Prelude> putStrLn "hello: привет"
hello: привет
Prelude> :q
Leaving GHCi.
$ hugs -98
__   __ __  __  ____   ___      _________________________________________
||   || ||  || ||  || ||__      Hugs 98: Based on the Haskell 98 standard
||___|| ||__|| ||__||  __||     Copyright (c) 1994-2005
||---||         ___||           World Wide Web: http://haskell.org/hugs
||   ||                         Bugs: http://hackage.haskell.org/trac/hugs
||   || Version: September 2006 _________________________________________

Hugs mode: Restart with command line option +98 for Haskell 98 mode

Type :? for help
Hugs> "hello: привет"
"hello: \1087\1088\1080\1074\1077\1090"
Hugs> 'Я'
'\1071'
Hugs> putStrLn "hello: привет"
hello: привет

Hugs> :q
[Leaving Hugs]
$ locale
LANG=ru_RU.UTF-8
LC_CTYPE="ru_RU.UTF-8"
LC_NUMERIC="ru_RU.UTF-8"
LC_TIME="ru_RU.UTF-8"
LC_COLLATE="ru_RU.UTF-8"
LC_MONETARY="ru_RU.UTF-8"
LC_MESSAGES="ru_RU.UTF-8"
LC_PAPER="ru_RU.UTF-8"
LC_NAME="ru_RU.UTF-8"
LC_ADDRESS="ru_RU.UTF-8"
LC_TELEPHONE="ru_RU.UTF-8"
LC_MEASUREMENT="ru_RU.UTF-8"
LC_IDENTIFICATION="ru_RU.UTF-8"
LC_ALL=
$ 

print結果をフォーマットするためにとが使用されているためだと推測できshowます。これらの関数は、標準的で移植性の高い方法でデータをフォーマットするために最善を尽くします。そのため、奇妙な文字をエスケープすることを好みます (おそらく、 Haskell の標準):

$ ghci
GHCi, version 7.0.1: http://www.haskell.org/ghc/  :? for help
Prelude> show 'Я'
"'\\1071'"
Prelude> :q
Leaving GHCi.
$ hugs -98
Type :? for help
Hugs> show 'Я'
"'\\1071'"
Hugs> :q
[Leaving Hugs]
$ 

しかしそれでも、GHCi や Hugs をハッキングして、これらの文字を人間が読める形式で (つまり、直接、エスケープせずに) 印刷する方法を知っていれば素晴らしいことです。これは、インタラクティブな Haskell 環境を教育目的で使用する場合、英語以外の聴衆の前で Haskell のチュートリアル/デモンストレーションを行う場合に高く評価されます。

実際、教育目的だけでなく、デバッグにも役立ちます。ASCII 以外の文字を使用して、他の言語の単語を表す文字列に対して定義された関数がある場合。そのため、プログラムが言語固有であり、別の言語の単語のみがデータとして意味を持ち、そのような単語に対してのみ定義された関数がある場合、GHCi でのデバッグではこのデータを確認することが重要です。

私の質問を要約すると:既存の対話型 Haskell 環境をハックして、結果に Unicode をよりわかりやすく表示する方法はありますか? (私の場合、「親しみやすい」とは「より単純」であることを意味します: printGHCi または Hugs で非ラテン文字をputChar、 , putStrLn、つまりエスケープされていない単純な直接的な方法で表示したいと思います。)

(おそらく、GHCi と Hugs98 に加えて、Haskell と対話するための既存の Emacs モードも見て、それらがきれいなエスケープされていない方法で結果を表示できるかどうかを確認します。)

4

7 に答える 7

21

これをハックする 1 つの方法は、標準出力を読み取り、Unicode 文字のエスケープを解除するシェル ラッパーに GHCi をラップすることです。もちろん、これはHaskellの方法ではありませんが、仕事はします:)

たとえば、これはandghci-escを使用するラッパーです(ここでは 3 が重要です)。shpython3

#!/bin/sh

ghci "$@" | python3 -c '
import sys
import re

def tr(match):
    s = match.group(1)
    try:
        return chr(int(s))
    except ValueError:
        return s

for line in sys.stdin:
    sys.stdout.write(re.sub(r"\\([0-9]{4})", tr, line))
'

の使用法ghci-esc:

$ ./ghci-esc
GHCi, version 7.0.2: http://www.haskell.org/ghc/  :? for help
> "hello"
"hello"
> "привет"
"привет"
> 'Я'
'Я'
> show 'Я'
"'\Я'"
> :q
Leaving GHCi.

上記のすべてのアンエスケープが正しく行われるわけではありませんが、これは Unicode 出力を視聴者に表示するための迅速な方法です。

于 2011-04-10T23:16:44.403 に答える
11

この問題にはある程度の進展がありました。bravit (Vitaly Bragilevsky) に感謝!:

おそらくGHC 7.6.1に組み込まれています。(それは...ですか?..)

キリル文字を今すぐ印刷する方法

GHCi に渡されるパラメータは、キリル文字を表示できる関数でなければなりません。Hackage ではそのような機能は見つかりませんでした。したがって、今のところ、単純なラッパーを作成する必要があります。

module UPPrinter where
import System.IO
import Text.PrettyPrint.Leijen

upprint a = (hPutDoc stdout . pretty) a >> putStrLn ""

そして、ghciこのように実行します:ghci -interactive-print=UPPrinter.upprint UPPrinter

もちろん、これは一度だけ に書き留めることができます.ghci

実用的な問題: 別のナイスを考え出すShow

さて、ここで実際的な問題があります: 標準の代わりに何を使用するかShow(標準Showは、私たちの希望に反して必要なシンボルをエスケープします)?

他人の作品の使用: その他のプリティプリンター

上記Text.PrettyPrint.Leijenが示唆されているのは、おそらく、文字列内のそのような記号をエスケープしないことが知られているためです。

Show に基づいた独自の Show -- 魅力的ですが、実用的ではありません

ここでの回答で提案されているようにShow、独自の を作成するのはどうですか。ShowGhci実用的ですか?...

代替Showクラス ( など) のインスタンスを定義する作業を節約するために、デフォルトで の既存のインスタンスを使用し、およびのインスタンスのみを再定義しShowGhciたくなるかもしれません。を使用すると、文字列を含む複雑なデータに対してold を呼び出して文字列を表示するために「ハードコンパイル」されるためです。この状況では、同じクラス インターフェイスを実装するさまざまな辞書を、このインターフェイスを使用する関数に渡す機能が求められます ( sub に渡すことになります)。これのための GHC 拡張機能はありますか?ShowStringCharshowGhci = showshowshowshowshow

Showと のインスタンスのみに基づいて再定義することは、 のように「普遍的」(広く適用可能) にしたい場合、あまり実用的ではありませCharん。StringShow

再解析show

より実用的な(そして短い)解決策は、別の回答にあります。出力を解析してshow文字と文字列を検出し、それらを再フォーマットします。(意味的には少し醜いように見えますが、ほとんどの場合、解決策は短く安全です( ; で他の目的に使用される引用符がない場合; のアイデアは多かれ少なかれあるshowため、標準的なものには当てはまりませんshow正しい解析可能な Haskell。)

プログラムのセマンティック型

そして、もう一つ指摘。

実際、GHCi でのデバッグに関心がある場合 (単に Haskell のデモを行い、きれいな出力を求めているのではなく)、非 ASCII 文字を表示する必要があるのは、プログラム内にこれらの文字が固有に存在するためです (そうでない場合は、デバッグのために、それらをラテン文字に置き換えるか、コードが表示されることをあまり気にしなくてもかまいません)。つまり、これらの文字または文字列には、問題領域の観点から何らかの意味があります。(たとえば、私は最近ロシア語の文法分析に取り組んでおり、サンプル辞書の一部としてのロシア語の単語は、私のプログラムに「本質的に」存在していました。その作業は、これらの特定の単語でのみ意味があります。デバッグ時にそれらを読んでください。)

しかし、文字列に何らかの意味がある場合、それらはもはや単純な文字列ではありません。意味のある型のデータです。おそらく、この種の意味を持つ特別な型を宣言すると、プログラムはより良く安全になるでしょう。

そして、万歳!Showこの型のインスタンスを定義するだけです。また、GHCi でプログラムをデバッグしても問題ありません。

例として、文法分析用のプログラムで、次のことを行いました。

newtype Vocable = Vocable2 { ortho :: String } deriving (Eq,Ord)
instance IsString Vocable -- to simplify typing the values (with OverloadedStrings)
    where fromString = Vocable2 . fromString

newtype Lexeme = Lexeme2 { lemma :: String } deriving (Eq,Ord)
instance IsString Lexeme -- to simplify typing the values (with OverloadedStrings)
    where fromString = Lexeme2 . fromString

(ここでの追加fromStringは、内部表現をStringtoByteStringなどに切り替える可能性があるためです)

showコードを作成するときにさまざまな種類の単語を混在させることができないため、それらを適切に実行できることは別として、より安全になりました。

于 2012-10-28T23:00:51.967 に答える
10

Ghciの次のバージョン7.6.1では、-interactive-printという新しいGhciオプションが提供されるため、状況が変わります。これはghc-manualからコピーされたものです:(そして私はmyShowとmyPrintを次のように書きました)

2.4.8. Using a custom interactive printing function

[New in version 7.6.1] By default, GHCi prints the result of expressions typed at the prompt using the function System.IO.print. Its type signature is Show a => a -> IO (), and it works by converting the value to String using show.

This is not ideal in certain cases, like when the output is long, or contains strings with non-ascii characters.

The -interactive-print flag allows to specify any function of type C a => a -> IO (), for some constraint C, as the function for printing evaluated expressions. The function can reside in any loaded module or any registered package.

As an example, suppose we have following special printing module:

     module SpecPrinter where
     import System.IO

     sprint a = putStrLn $ show a ++ "!"

The sprint function adds an exclamation mark at the end of any printed value. Running GHCi with the command:

     ghci -interactive-print=SpecPrinter.sprinter SpecPrinter

will start an interactive session where values with be printed using sprint:

     *SpecPrinter> [1,2,3]
     [1,2,3]!
     *SpecPrinter> 42
     42!

A custom pretty printing function can be used, for example, to format tree-like and nested structures in a more readable way.

The -interactive-print flag can also be used when running GHC in -e mode:

     % ghc -e "[1,2,3]" -interactive-print=SpecPrinter.sprint SpecPrinter
     [1,2,3]!


module MyPrint (myPrint, myShow) where
-- preparing for the 7.6.1
myPrint :: Show a => a -> IO ()
myPrint = putStrLn . myShow

myShow :: Show a => a -> String
myShow x = con (show x) where
  con :: String -> String
  con [] = []
  con li@(x:xs) | x == '\"' = '\"':str++"\""++(con rest)
                | x == '\'' = '\'':char:'\'':(con rest')
                | otherwise = x:con xs where
                  (str,rest):_ = reads li
                  (char,rest'):_ = reads li

そして、それらはうまく機能します:

*MyPrint> myPrint "asf萨芬速读法"
"asf萨芬速读法"
*MyPrint> myPrint "asdffasdfd"
"asdffasdfd"
*MyPrint> myPrint "asdffa撒旦发"
"asdffa撒旦发"
*MyPrint> myPrint '此'
'此'
*MyPrint> myShow '此'
"'\27492'"
*MyPrint> myPrint '此'
'此'
于 2013-01-22T15:20:57.523 に答える
7

オプション 1 (悪い):

次のコード行を変更します。

https://github.com/ghc/packages-base/blob/ba98712/GHC/Show.lhs#L356

showLitChar c s | c > '\DEL' =  showChar '\\' (protectEsc isDec (shows (ord c)) s)

そしてghcを再コンパイルします。

オプション 2 (多くの作業):

GHCi 型が解析されたステートメントをチェックすると、tcRnStmt依存するものに なりますmkPlan(どちらもhttps://github.com/ghc/ghc/blob/master/compiler/typecheck/TcRnDriver.lhsにあります)。これは、入力されたステートメントのいくつかのバリアントをタイプ チェックしようとします。

let it = expr in print it >> return [coerce HVal it]

具体的には:

print_it  = L loc $ ExprStmt (nlHsApp (nlHsVar printName) (nlHsVar fresh_it))
                                      (HsVar thenIOName) placeHolderType

ここで変更する必要があるのはprintName(にバインドするSystem.IO.print) だけです。printGhci代わりに、次のように実装されたものにバインドされている場合:

class ShowGhci a where
    showGhci :: a -> String
    ...

-- Bunch of instances?

instance ShowGhci Char where
    ...  -- The instance we want to be different.

printGhci :: ShowGhci a => a -> IO ()
printGhci = putStrLn . showGhci

Ghci は、さまざまなインスタンスをコンテキストに持ち込むことで、出力される内容を変更できます。

于 2011-04-11T12:26:41.767 に答える
4

IO に「テキスト」パッケージを使用するように切り替えることができます。例えば

Prelude> :set -XOverloadedStrings
Prelude> Data.Text.IO.putStrLn "hello: привет"
hello: привет

このパッケージは標準 Haskell ディストリビューションであるHaskell Platformの一部であり、IO 操作を備えた効率的なパックされた不変の Unicode テキスト型を提供します。多くのエンコーディングがサポートされています。

.ghci ファイルを使用すると、デフォルトで -XOverloadStrings をオンに設定し、値を表示するコマンド:defを導入するマクロを作成できます。それはうまくいくでしょう。:texttext

于 2011-04-11T00:20:39.693 に答える
4

ghci の を知ったので-interactive-print、これは素晴らしい機能です。質問と回答を書いてくれてありがとう!ところで、私が Web で見つけることができる既存のきれいなプリンターにはいくつかのまれなケースがあり、適切な Unicode を記述する問題は思っshowたよりも複雑であることが判明しました。

したがって、この目的のためにHaskell パッケージの unicode-showを作成することにしまし

このパッケージが、この Q&A を検索した人々の役に立てば幸いです :)

于 2016-02-04T04:53:49.703 に答える
3

理想的なのは ghci へのパッチで、ユーザーが:set以外の結果を表示する関数を使用できるようにすることですshow。そのような機能は現在存在しません。ただし、:defマクロに関する Don の提案 (テキスト パッケージの有無にかかわらず) はまったく悪くありません。

于 2011-04-11T19:58:26.503 に答える