2011-10-16 1 views
3

私は最近Haskellのを学習し、私はHaskellの中のpythonでの面接のためにした何かを書き換えしようとして始めました。私はキャメルケースの文字列を分離したアンダースコア( "myVariableName" - > "my_variable_name")に変換しようとしていますが、最初の文字が大文字の場合はエラーもスローされます。ここで初心者Haskellの問題(のための...から生じるノーインスタンス)

は、私が持っているものです。

import qualified Data.Char as Char 

translate_java :: String -> String 
translate_java xs = translate_helper $ enumerate xs 
    where 
     translate_helper [] = [] 
     translate_helper ((a, num):xs) 
      | num == 1 and Char.isUpper a = error "cannot start with upper" 
      | Char.isUpper a    = '_' : Char.toLower a : translate_helper xs 
      | otherwise     = a : translate_helper xs 


enumerate :: (Num b, Enum b) => [a] -> [(a,b)] 
enumerate xs = zip xs [1..] 

私はそれは私が奇妙な方法でこのことについてつもりだ、と私はこれを実装するためのより良い方法についてのアドバイスをしてみたいが、私が好きな、かなり可能性があります実現しますこれをコンパイルすることもできます。ここには私が今いるエラーがあります:

Prelude> :r 
[1 of 1] Compiling Main    (translate.hs, interpreted) 

translate.hs:4:20: 
    No instance for (Num 
         (([Bool] -> Bool) -> (Char -> Bool) -> Char -> t)) 
     arising from a use of `translate_helper' at translate.hs:4:20-35 
    Possible fix: 
     add an instance declaration for 
     (Num (([Bool] -> Bool) -> (Char -> Bool) -> Char -> t)) 
    In the first argument of `($)', namely `translate_helper' 
    In the expression: translate_helper $ enumerate xs 
    In the definition of `translate_java': 
     translate_java xs 
         = translate_helper $ enumerate xs 
         where 
          translate_helper [] = [] 
          translate_helper ((a, num) : xs) 
               | num == 1 and Char.isUpper a 
               = error "cannot start with upper 
" 
               | Char.isUpper a 
               = '_' : Char.toLower a : transla 
te_helper xs 
               | otherwise = a : translate_help 
er xs 
Failed, modules loaded: none. 

ここで起こっていることの説明は素晴らしいでしょう。私は本当に "(Bool - > Bool) - >(Char - > Bool) - > Char - > t))がどこから来ているのか分かりません。私はtranslate_helperの型宣言は[(a、b)] - > [a]のようなものだろうと思いますか?

答えて

4

問題は、このです:

| num == 1 and Char.isUpper a = ... 

andは中置演算子ではありません。むしろ、それは関数です:

and :: [Bool] -> Bool 

だから、「機能」1に3つの引数を適用するよう1 and Char.isUpper aを解釈しています。代わりに&&を使用してください。

エラーメッセージは数字が解釈される方法に由来します。数字、例えば1は実際には多形である。取得する特定のタイプは、必要なタイプによって異なります。だからx+1と言うことができ、それはxが整数か二重であるかどうかに関係なく動作します。だから、コンパイラは1のタイプは3引数の関数であることが必要であると推論して、それが(当然、失敗した、と)その型に1を変換できる数値型のマッチングを見つけることを試みました。

+0

速い応答のおかげで、私は永遠にその機能を見て過ごした、私はこれまでにそれをキャッチしたとは思わない。 1も多型であるという良い説明。 – cgag

5

あなたは&&andを交換する必要があります。最初のものは、ブール値のリストを受け取り、それらのすべてを計算する関数(接頭辞)です。 2番目は真の論理的なものです。エラーメッセージはちょっと混乱します。このような奇妙なエラーメッセージが出るときはいつも、自分のコードに型シグネチャを付けるようになっています。コンパイラは、何が問題になったのかをより詳細に説明します。

+0

私はそれをしようとしていましたが、型署名を追加しましたが、私が事態を悪化させていないと自信がなかったことがわかりました。 translate_helperの型シグネチャには何を置きますか?私は[(a、b)] - > [a]を持っていましたが、charがtypeclassではないので、aがCharであることを指定する方法がわかりませんでした。 – cgag

+1

@cgag: 'a'が常に' Char'になっているとすれば、 'a(char、b)] - > [Char]'という型シグネチャで 'a'を' Char'に置き換えることができます。 – hammar

5

andの代わりに(&&)を使用する必要がありますので、他の質問にお答えします。いいえ、あなたはこれを奇妙な方法で行うつもりはありません。

しかし...私はそれがさらにエレガントなことができると思います!

translate_java (x:xs) | isUpper x = error "cannot start with an upper" 
translate_java xs = concatMap translate xs where 
    translate x = ['_' | isUpper x] ++ [toLower x] 

ここで起こっていくつか面白いものがあります:

  1. 特殊な場合は、すぐにチェックされています。これを行うために再帰するまで待たないでください!
  2. concatMap機能は、多くのケースでは本当に便利です。 mapの後にはconcatが続きます。もし私がこれを自分で書いていたら、代わりにxs >>= translateを使うでしょう。
  3. その['_' | isUpper x]はリスト内包です。これは、述語が保持されているかどうかに応じて、0または1要素のリストを作成するためのかわいいイディオムです。

これ以外のコードは、わかりやすく示す必要があります。

3

私の解決策です。ダニエル・ワグナーがconcatMapとリストの理解を使って与えた答えほど巧妙ではありませんが、おそらく初心者にとっては理解しやすいでしょう。

conv :: String -> String 
conv [] = [] 
conv [email protected](x:xs) = if Char.isUpper x 
       then error "First character cannot be uppercase" 
       else change s 

change :: String -> String 
change [] = [] 
change (x:xs) = if Char.isUpper x 
       then '_' : Char.toLower x : change xs 
       else x : change xs 

関数CONVは、実際には、最初の文字が大文字であってはならないことを、あなたの基準をチェックし、そうでない場合には、作業を行う機能変更、文字列に引き渡します。これは、すべての文字を1つずつ順番に並べてリストを作成します。文字が大文字の場合は、アンダースコアの後に小文字の文字が追加されます。そうでない場合は、文字が既に小文字の場合はそのまま追加されます。