와 Data.Array Int a
및 es!!
같은과 [a]
을 대체 할 거라고는되는 N × N 그리드를 가지고 모든 수평을 추출 할 , 길이 M의 수직선과 대각선을 긋고 최대 곱을 구하십시오. 라인 길이가 2 인 상태의 예를 4x4 그리드에 대한 몇 가지 하스켈 기술을 설명하자
chunks 2 [1,2,3,4] == [[1,2],[2,3],[3,4]]
:
이
[[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9,10,11,12],
[13,14,15,16]]
수평 및 수직 쉽게, 당신이 필요로하는 모든이 목록에서 길이 M의 덩어리를 추출하는 기능입니다
해당 기능의 유형은 [a] -> [[a]]
입니다.이것은 목록과 관련된 함수이므로 휠을 다시 만들기 전에 Data.List과 비슷한 내용이 있는지 살펴 보겠습니다. 아하, tails
는 제거 목록의 시작 부분에서 더 많은 요소 목록을 반환 유사하다 : 단지 우리가 길이 2의를 만들기 위해 하위 목록을 단축 할 수 있다면
tails [1,2,3,4] == [[1,2,3,4],[2,3,4],[3,4],[4],[]]
하지만 우리가 할 수있는, map
를 사용하여 원래 작업이 가장 큰 제품을 찾을 수 있으며, [15, N]
≥의 제품으로 난 작은 라인에 대해 걱정하지 것이다
map (take n) (tails xs) -- [[1,2],[2,3],[3,4],[4],[]]
:리스트의 모든 원소에 함수를 적용하고 새로운 목록을 반환 기능, [15]
의 제품, N ≥ 1. 그러나 당신이 그 (것)들을 제거하고 싶은 경우에, th 길이 N의 목록에는 길이 M의 N-M + 1 청크가 포함되어 있으므로 take (4-2+1)
을 결과 목록에 적용 할 수 있습니다. 또는 당신이 할 수 단순히 filter 목록 :
chunks n xs = filter ((==n) . length) $ map (take n) (tails xs)
-- [[1,2],[2,3],[3,4]]
좋아, 우리는 목록에서 덩어리의 목록을 추출 할 수 있지만, 우리는 2D 그리드가 아닌 단순 목록을 가지고! 결과 코드는 별도의 목록에 덩어리를두고, 우리는 실제로 상관하지 않는 선에서 덩어리가 시작된 않는 한 그것은 일을 복잡하게
map (chunks 2) grid -- [[[1,2],[2,3],[3,4]],[[5,6],[6,7],[7,8]],...]
을하지만, 여기에 일이 : map
다시 우리를 구출. 그래서 우리는 concat . map
또는 이에 상응하는 concatMap
에 의해 한 단계 결과 목록을 평평하게 할 것입니다 :
concatMap (chunks 2) grid -- [[1,2],[2,3],[3,4],[5,6],[6,7],[7,8],...]
을 이제 어떻게 그리드에서 수직 덩어리를받을 수 있나요? 당신은 즉, 당신은 전체 그리드를 transpose 수 있다는 걸 행으로 열과 행을 열로 회전 할 때까지, 처음에는 무서운 소리, 다음 같은 코드 적용
concatMap (chunks 2) (transpose grid) -- [[1,5],[5,9],[9,13],[2,6],[6,10],...]
이제 어려운 부분 : 대각선 라인을. Norman Ramsey은 0 번 줄에 0 개의 요소를 떨어 뜨리고 1 번 줄에 1 개의 요소를 떨어 뜨릴 수 있다면 어떨까요? 대각선은 추출하기 쉬운 수직선이됩니다. map
을 사용하는 목록의 모든 요소에 함수를 적용하려면 각 요소에 다른 함수 (예 : drop 0
, drop 1
, drop 2
등)를 적용해야합니다. map
은 적합하지 않습니다. 그러나보기, drop
의 첫 번째 인수는 무한한 목록 [0..]
으로 표시 될 수있는 연속적인 숫자의 패턴을 형성합니다. 이제 우리가 하나의 요소를 취할 수 있다면 어떨까요? [0..]
무한한 목록 [0..]
의 번호와 그리드의 행을 취하고이 번호가있는 drop
을 행에 적용하는 함수가 필요합니다. zipWith
는 당신이 필요로하는 무엇을 :
zipWith drop [0..] grid -- [[1,2,3,4],[6,7,8],[11,12],[16]]
map head $ zipWith drop [0..] grid -- [1,6,11,16]
하지만 길이 2의 모든 대각선뿐만 아니라 가장 큰 대각선을 원한다. 그리드를보고 생각해보십시오. 행 0의 요소로 볼 때 대각선은 무엇입니까? [1,6],[2,7],[3,8]
. 그래서 당신이 요소 만 처음 2 개 행을 받아 전치 할 필요가 분명하다 :
transpose $ zipWith drop [0,1] grid -- [[1,6],[2,7],[3,8],[4]]
을 지금뿐만 아니라 다른 행부터 대각선을 얻는 방법? 우리 tails
의 트릭을 기억하십니까?
concatMap (transpose . zipWith drop [0,1]) (tails g)
-- [[1,6],[2,7],[3,8],[5,10],[6,11],...]
을하지만 이들은에서 이동에만 대각선입니다 왼쪽 상단 오른쪽 아래에 : 우리는 concatMap
에 새로운 기능을 제공하고 tails grid
에 적용하여 모든 대각선을 얻을 수 있습니다. 오른쪽 상단에서 왼쪽 하단으로 이동하는 것은 어떻습니까? 그것은 쉬운 그냥 그리드의 행을 반대합니다 :
concatMap (transpose . zipWith drop [0,1]) (tails $ reverse g)
-- [[13,10],[14,11],[15,12],[9,6],[10,7],...]
마지막으로, 당신은 모든 라인의 제품을 찾아 가장 큰를 선택해야합니다. 최종 코드는 다음과 같습니다.
grid = [[1..4],[5..8],[9..12],[13..16]]
chunks n xs = map (take n) (tails xs)
horizontal = concatMap (chunks 2) grid
vertical = concatMap (chunks 2) (transpose grid)
grave = concatMap (transpose . zipWith drop [0,1]) (tails grid)
acute = concatMap (transpose . zipWith drop [0,1]) (tails $ reverse grid)
maxProduct = maximum $ map product $ horizontal ++ vertical ++ grave ++ acute
-- answer: 240
이 코드는 최대한 우아하고 효율적입니까? 지옥 아니,하지만 작동 및 함수 프로그래밍의 생각의 특정 패턴을 보여줍니다. 처음에는 읽기 쉽고 일반적인 솔루션에 도달 할 때까지 작동하는 코드를 작성한 다음 반복적으로 리팩터링해야합니다.
오일러 문제 번호도 함께 지정하십시오. 어떤 사람들은 이미 그것을 풀었고 자신의 해결책을보고 싶은데 아마도 그것에 기초한 유용한 답을 줄 것입니다. – yairchu
문제 # 11 – MtnViewMark