답변:
봐 여기에 , 사용되는 연산자입니다 !!.
즉 , 목록이 0 인덱싱되기 때문에 [1,2,3]!!1제공합니다 2.
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 , !!첫 번째 결과로 (유형 서명에는 실제로 검색 한 것과 비교하여 두 개의 인수가 반대로 있음) 깔끔 하죠?
또한 코드가 목록의 맨 앞에서 소비하는 대신 인덱싱에 의존하는 경우 목록이 실제로 적절한 데이터 구조가 아닐 수 있습니다. O (1) 인덱스 기반 액세스의 경우 배열 또는 벡터 와 같은보다 효율적인 대안이 있습니다 .
사용에 대한 대안 (!!)은
렌즈 패키지와 그 element기능 및 관련 작업자를 사용하는 것입니다.
렌즈 위에 나열 이후 구조 및 중첩 구조의 다양한 액세스 균일 한 인터페이스를 제공한다. 아래에서는 예제를 제공하는 데 중점을두고 유형 서명과 렌즈 패키지 의 이론에 대해 설명
합니다. 이론에 대해 더 알고 싶다면 github repo 의 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
중위 연산자로 목록에 액세스하려면
> [1,2,3,4,5] ^? element 2 -- 0 based indexing
Just 3
이와 달리 (!!)경계를 벗어난 요소에 액세스 할 때 예외가 발생하지 Nothing않고 대신 반환 됩니다. 코너 케이스가 더 많고 런타임 오류가 발생할 가능성이 더 높기 때문에 (!!)또는 같은 부분 기능을 피하는 것이 좋습니다 head. 이 위키 페이지 에서 부분 기능을 피해야하는 이유에 대해 조금 더 읽을 수 있습니다 .
> [1,2,3] !! 9
*** Exception: Prelude.(!!): index too large
> [1,2,3] ^? element 9
Nothing
렌즈 기술을 부분 함수로 강제하고 (^?!)연산자 대신 연산자를 사용하여 범위를 벗어난 경우 예외를 throw 할 (^?)수 있습니다.
> [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 문서에서 Traversables 예제의 더 긴 목록을 참조하세요 . .
렌즈 해킹 으로 중첩 된 구조를 파헤치는 것은 간단합니다 . 예를 들어 목록 목록의 요소에 액세스 :
> [[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요구 사항 을 충족하는 한 임의의 유형으로 임의로 깊이 중첩 할 수 있습니다 . 따라서 텍스트 시퀀스 트리 목록에 액세스하는 것은 땀이 나지 않습니다.
많은 언어에서 일반적인 작업은 배열의 인덱스 된 위치에 할당하는 것입니다. 파이썬에서는 다음을 수행 할 수 있습니다.
>>> a = [1,2,3,4,5]
>>> a[3] = 9
>>> a
[1, 2, 3, 9, 5]
렌즈 패키지는이 기능을 제공 (.~)연산자. 파이썬과 달리 원래 목록은 변경되지 않고 새 목록이 반환됩니다.
> 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]
할당은 Traversables의 임의 중첩으로 다시 완벽하게 작동합니다 .
> [[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. 하스켈이있는 바로 그 이유는 그러한 런타임 오류를 피하기 위해서입니다!