목록에서 n 번째 요소를 어떻게 얻을 수 있습니까?


97

이 C 코드와 유사한 Haskell의 인덱스로 목록에 액세스하려면 어떻게해야합니까?

int a[] = { 34, 45, 56 };
return a[1];

답변:


154

여기에 , 사용되는 연산자입니다 !!.

즉 , 목록이 0 인덱싱되기 때문에 [1,2,3]!!1제공합니다 2.


86
개인적으로 Maybe 타입을 반환하지 않는 at-index 접근자가 관용적 인 Haskell로 어떻게 받아 들여 지는지 이해할 수 없습니다. [1,2,3]!!6런타임 오류가 발생합니다. !!유형이 있으면 매우 쉽게 피할 수 있습니다 [a] -> Int -> Maybe a. 하스켈이있는 바로 그 이유는 그러한 런타임 오류를 피하기 위해서입니다!
worldsayshi

9
트레이드 오프입니다. 그들이 선택한 상징은 아마도 그들이 가질 수있는 가장 놀라운 상징 일 것입니다. 그래서 저는 그 아이디어가 엣지 케이스를 허용하지만 비 관상적인 것으로 눈에 띄게 만드는 것이라고 생각합니다.
cdosborn

3
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)). 이는 무한 목록에서 치명적인 오류로 실패합니다.
djvs

2
!!부분적이므로 안전하지 않은 기능입니다. 아래 주석을 살펴보고 lens stackoverflow.com/a/23627631/2574719를
goetzc

90

질문이나 답변에 문제가 있다고 말하는 것이 아닙니다 . 미래에 시간을 절약 할 수있는 Hoogle 의 멋진 도구에 대해 알고 싶을 수도 있습니다. Hoogle 을 사용하면 표준 라이브러리 기능을 검색 할 수 있습니다. 주어진 서명과 일치합니다. 따라서에 대해 아무것도 알지 못하는 !!경우 "무엇 Int이든 목록 을 가져 와서 하나를 반환하는 것"을 검색 할 수 있습니다 . 즉

Int -> [a] -> a

Lo 및 behold , !!첫 번째 결과로 (유형 서명에는 실제로 검색 한 것과 비교하여 두 개의 인수가 반대로 있음) 깔끔 하죠?

또한 코드가 목록의 맨 앞에서 소비하는 대신 인덱싱에 의존하는 경우 목록이 실제로 적절한 데이터 구조가 아닐 수 있습니다. O (1) 인덱스 기반 액세스의 경우 배열 또는 벡터 와 같은보다 효율적인 대안이 있습니다 .


4
Hoogle은 절대적으로 훌륭합니다. 모든 Haskell 프로그래머는 그것을 알아야합니다. Hayoo ( holumbus.fh-wedel.de/hayoo/hayoo.html ) 라는 대안이 있습니다 . 입력하는대로 검색하지만 Hoogle만큼 영리하지 않은 것 같습니다.
musiKk 2011 년

61

사용에 대한 대안 (!!)렌즈 패키지와 그 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요구 사항 을 충족하는 한 임의의 유형으로 임의로 깊이 중첩 할 수 있습니다 . 따라서 텍스트 시퀀스 트리 목록에 액세스하는 것은 땀이 나지 않습니다.


n 번째 요소 변경

많은 언어에서 일반적인 작업은 배열의 인덱스 된 위치에 할당하는 것입니다. 파이썬에서는 다음을 수행 할 수 있습니다.

>>> 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]]

3
Data.Traversable에서 다시 내보내기보다는에 링크를 제안 해도 lens될까요?
dfeuer 2015 년

@dfeuer-기본에서 Data.Traversable에 대한 링크를 추가했습니다. 나는 또한 이전 링크를 유지하고 Lens 문서에 더 긴 예제 순회 가능 목록이 있다고 지적했습니다. 제안 해 주셔서 감사합니다.
Davorak 2015 년

11

정답은 이미 주어졌습니다. 사용 !! .

그러나 초보자는 종종이 연산자를 과도하게 사용하는 경향이 있습니다. Haskell에서는 비용이 많이 듭니다 (배열이 아닌 단일 연결 목록에서 작업하기 때문입니다). 이를 방지하는 몇 가지 유용한 기술이 있으며 가장 쉬운 방법은 zip을 사용하는 것입니다. 를 작성 zip ["foo","bar","baz"] [0..]하면 한 쌍의 각 요소에 "첨부 된"색인이있는 새 목록을 얻게됩니다. [("foo",0),("bar",1),("baz",2)]이는 종종 정확히 필요한 것입니다.


2
거기에서 당신의 유형에 대해서도 조심해야합니다. 대부분의 경우 인덱스가 빠른 머신 Int보다 느린 Integer로 끝나는 것을 원하지 않습니다. 함수가 정확히 무엇을하고 입력이 얼마나 명시 적인지에 따라 Haskell은 [0 ..]의 유형을 [Int] 대신 [Integer]로 추론 할 수 있습니다.
chrisdb 2011 년

4

을 사용할 수 !!있지만 재귀 적으로 수행하려면 다음 방법 중 하나가 있습니다.

dataAt :: Int -> [a] -> a
dataAt _ [] = error "Empty List!"
dataAt y (x:xs)  | y <= 0 = x
                 | otherwise = dataAt (y-1) xs

4

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

이 함수는 음수 및 양수가 아닌 Int에 대해 각각 영원히 반복됩니다.
Bjartur Thorlacius
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.