回答:
itemOf :: Int -> [a] -> Maybe a; x `itemOf` xs = let xslen = length xs in if ((abs x) > xslen) then Nothing else Just (xs !! (x `mod` xslen))
。これは、無限リストでは破滅的に失敗することに注意してください。
あなたの質問や答えに問題があると言っているのではありませんが、Hoogleが将来的に時間を節約できる素晴らしいツールについて知りたいと思うかもしれません。Hoogleでは、標準のライブラリ関数を検索できます。特定の署名と一致します。したがって、について何も知らない!!
場合、あなたのケースでは、「Int
そして何でものリストを取り、単一のものを返すもの」を検索するかもしれません、すなわち
Int -> [a] -> a
Loとbehold、!!
最初の結果として(型シグネチャは実際には、検索したものと比較して2つの引数が逆になっていますが)。きちんとね?
また、コードが(リストの先頭から使用するのではなく)インデックス作成に依存している場合、リストは実際には適切なデータ構造ではない可能性があります。O(1)インデックスベースのアクセスの場合、配列やベクトルなど、より効率的な代替手段があります。
を使用する代わりに(!!)
、レンズパッケージとそのelement
機能および関連する演算子を使用することもできます
。
レンズは、リスト上以降構造およびネストされた構造の広範囲にアクセスするための均一なインタフェースを提供します。以下では、例の提供に焦点を当て、型のシグネチャとレンズパッケージの背後にある理論の両方について説明し
ます。理論について詳しく知りたい場合は、まずgithubリポジトリの readmeファイルから始めてください。
コマンドラインで:
$ cabal install lens
$ ghci
GHCi, version 7.6.3: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
> import Control.Lens
infix演算子を使用してリストにアクセスするには
> [1,2,3,4,5] ^? element 2 -- 0 based indexing
Just 3
これとは異なり、(!!)
これは範囲外の要素にアクセスするときに例外をスローせず、Nothing
代わりに戻ります。多くの場合のように、部分的な機能を避けるためにお勧めしている(!!)
かをhead
、彼らはより多くのコーナーケースを持っているし、実行時エラーが発生する可能性が高いからです。このwikiページで、部分的な機能を回避する理由についてもう少し読むことができます。
> [1,2,3] !! 9
*** Exception: Prelude.(!!): index too large
> [1,2,3] ^? element 9
Nothing
(^?!)
演算子の代わりに演算子を使用することにより、レンズテクニックを部分関数にして、範囲外の場合に例外をスローすることができ(^?)
ます。
> [1,2,3] ^?! element 1
2
> [1,2,3] ^?! element 9
*** Exception: (^?!): empty Fold
ただし、これはリストに限定されません。たとえば、同じ手法が標準の コンテナパッケージのツリーで機能します。
> import Data.Tree
> :{
let
tree = Node 1 [
Node 2 [Node 4[], Node 5 []]
, Node 3 [Node 6 [], Node 7 []]
]
:}
> putStrLn . drawTree . fmap show $tree
1
|
+- 2
| |
| +- 4
| |
| `- 5
|
`- 3
|
+- 6
|
`- 7
これで、深さ優先の順序でツリーの要素にアクセスできます。
> tree ^? element 0
Just 1
> tree ^? element 1
Just 2
> tree ^? element 2
Just 4
> tree ^? element 3
Just 5
> tree ^? element 4
Just 3
> tree ^? element 5
Just 6
> tree ^? element 6
Just 7
コンテナーパッケージから シーケンスにアクセスすることもできます。
> import qualified Data.Sequence as Seq
> Seq.fromList [1,2,3,4] ^? element 3
Just 4
ベクトルパッケージからの標準のintインデックス配列、標準テキストパッケージからの テキスト、標準 バイト文字列パッケージからのバイト文字列、および他の多くの標準データ構造にアクセスできます 。この標準的なアクセス方法は、Taversable タイプクラスのインスタンスにすることで、個人のデータ構造に拡張できます。Lensのドキュメントで、Traversableの例の長いリストを参照してください。。
ネストされた構造を掘り下げるのは、レンズハッカーを使用すると簡単です。たとえば、リストのリストの要素にアクセスする場合:
> [[1,2,3],[4,5,6]] ^? element 0 . element 1
Just 2
> [[1,2,3],[4,5,6]] ^? element 1 . element 2
Just 6
この構成は、ネストされたデータ構造のタイプが異なる場合でも機能します。たとえば、木のリストがあった場合:
> :{
let
tree = Node 1 [
Node 2 []
, Node 3 []
]
:}
> putStrLn . drawTree . fmap show $ tree
1
|
+- 2
|
`- 3
> :{
let
listOfTrees = [ tree
, fmap (*2) tree -- All tree elements times 2
, fmap (*3) tree -- All tree elements times 3
]
:}
> listOfTrees ^? element 1 . element 0
Just 2
> listOfTrees ^? element 1 . element 1
Just 4
Traversable
要件を満たす限り、任意の型を任意に深くネストすることができます。したがって、テキストのシーケンスのツリーのリストにアクセスするのは簡単です。
多くの言語で一般的な操作は、配列のインデックス位置に割り当てることです。Pythonでは次のようになります。
>>> a = [1,2,3,4,5]
>>> a[3] = 9
>>> a
[1, 2, 3, 9, 5]
レンズパッケージはと、この機能を提供します(.~)
演算子。Pythonとは異なり、元のリストは変更されませんが、新しいリストが返されます。
> let a = [1,2,3,4,5]
> a & element 3 .~ 9
[1,2,3,9,5]
> a
[1,2,3,4,5]
element 3 .~ 9
は単なる機能であり、レンズパッケージの(&)
一部であるオペレーターは、
単に逆の機能アプリケーションです。ここでは、より一般的な関数アプリケーションについて説明します。
> (element 3 .~ 9) [1,2,3,4,5]
[1,2,3,9,5]
代入は、Traversable
sの任意の入れ子で完全にうまく機能します。
> [[1,2,3],[4,5,6]] & element 0 . element 1 .~ 9
[[1,9,3],[4,5,6]]
Data.Traversable
での再エクスポートではなく、へのリンクを提案することはできlens
ますか?
正解はすでに与えられています:を使用してください!!
。
ただし、初心者はこの演算子を使いすぎる傾向があります。これはHaskellではコストがかかります(配列ではなく単一のリンクリストで作業するため)。これを回避するにはいくつかの便利なテクニックがありますが、最も簡単な方法はzipを使用することです。と書くzip ["foo","bar","baz"] [0..]
と、ペアの各要素に「添付」されたインデックスを持つ新しいリストを取得し[("foo",0),("bar",1),("baz",2)]
ます。これは、多くの場合、まさに必要なものです。
Haskellの標準リストのデータ型 forall t. [t]
実装におけるは、標準のCリンクリストに非常によく似ており、本質的にプロパティを共有しています。リンクリストは配列とは大きく異なります。最も注目すべきは、インデックスによるアクセスは、O(1)の定数時間操作ではなく、O(n)の線形アクセスです。
頻繁なランダムアクセスが必要な場合は、Data.Array
標準を検討してください。
!!
安全でない部分的に定義された関数であり、範囲外のインデックスのクラッシュを引き起こします。標準ライブラリには、いくつかのこのような部分の機能を(含まれていることに注意してくださいhead
、last
など)。安全のため、オプションタイプMaybe
またはSafe
モジュールを使用してください。
合理的に効率的で堅牢な合計(インデックス≥0の場合)インデックス関数の例:
data Maybe a = Nothing | Just a
lookup :: Int -> [a] -> Maybe a
lookup _ [] = Nothing
lookup 0 (x : _) = Just x
lookup i (_ : xs) = lookup (i - 1) xs
リンクされたリストを操作する場合、通常は序数が便利です。
nth :: Int -> [a] -> Maybe a
nth _ [] = Nothing
nth 1 (x : _) = Just x
nth n (_ : xs) = nth (n - 1) xs
[1,2,3]!!6
ランタイムエラーが発生します。!!
タイプがあれば非常に簡単に回避できます[a] -> Int -> Maybe a
。私たちがHaskellを持っているまさにその理由は、そのような実行時エラーを回避することです!