int
임의의 중첩 목록의 합계를 반환하는 F # 함수를 만들려고합니다 . 즉. a list<int>
, a list<list<int>>
및 a 에서 작동 합니다 list<list<list<list<list<list<int>>>>>>
.
Haskell에서 나는 다음과 같이 쓸 것입니다 :
class HasSum a where
getSum :: a -> Integer
instance HasSum Integer where
getSum = id
instance HasSum a => HasSum [a] where
getSum = sum . map getSum
내가 할 수있는 것 :
list :: a -> [a]
list = replicate 6
nestedList :: [[[[[[[[[[Integer]]]]]]]]]]
nestedList =
list $ list $ list $ list $ list $
list $ list $ list $ list $ list (1 :: Integer)
sumNestedList :: Integer
sumNestedList = getSum nestedList
F #에서 어떻게 이것을 달성 할 수 있습니까?
답변
최신 정보
($)
멤버 대신 연산자 를 사용하여 더 간단한 버전을 찾았습니다 . https://stackoverflow.com/a/7224269/4550898에서 영감을 얻었습니다 .
type SumOperations = SumOperations
let inline getSum b = SumOperations $ b // <-- puting this here avoids defaulting to int
type SumOperations with
static member inline ($) (SumOperations, x : int ) = x
static member inline ($) (SumOperations, xl : _ list) = xl |> List.sumBy getSum
나머지 설명은 여전히 적용되며 유용합니다 …
가능한 방법을 찾았습니다.
let inline getSum0< ^t, ^a when (^t or ^a) : (static member Sum : ^a -> int)> a : int =
((^t or ^a) : (static member Sum : ^a -> int) a)
type SumOperations =
static member inline Sum( x : float ) = int x
static member inline Sum( x : int ) = x
static member inline Sum(lx : _ list) = lx |> List.sumBy getSum0<SumOperations, _>
let inline getSum x = getSum0<SumOperations, _> x
2 |> getSum |> printfn "%d" // = 2
[ 2 ; 1 ] |> getSum |> printfn "%d" // = 3
[[2; 3] ; [4; 5] ] |> getSum |> printfn "%d" // = 14
예제를 실행 :
let list v = List.replicate 6 v
1
|> list |> list |> list |> list |> list
|> list |> list |> list |> list |> list
|> getSum |> printfn "%d" // = 60466176
이것은 회원 제약 SRTPs를 사용을 기반으로합니다 static member Sum
, 제약라는 멤버가하는 유형을 필요로 Sum
하는 다시 표시를 int
. SRTP를 사용할 때 일반 기능은이어야 inline
합니다.
그것은 어려운 부분이 아닙니다. 어려운 부분은 Sum
기존 유형과 유사 int
하고 List
허용되지 않는 멤버를 “추가” 하는 것입니다. 그러나 새로운 유형에 추가 할 수 있으며 항상
어디에 SumOperations
있을지 제약 조건에 포함시킬 수 있습니다 .(^t or ^a)
^t
SumOperations
getSum0
Sum
멤버 제약 조건을 선언 하고 호출합니다.getSum
통과SumOperations
하는 제 1 타입 파라미터로서getSum0
이 줄 static member inline Sum(x : float ) = int x
은 컴파일러가 static member inline Sum(x : int )
호출 할 때 기본값이 아닌 일반적인 동적 함수 호출을 사용하도록 설득하기 위해 추가되었습니다.List.sumBy
보시다시피 약간 복잡하지만 구문이 복잡하고 컴파일러의 단점을 해결해야했지만 결국 가능했습니다.
이 메소드는 다음에 더 많은 정의를 추가하여 배열, 튜플, 옵션 등을 조합하여 확장 할 수 있습니다 SumOperations
.
type SumOperations with
static member inline ($) (SumOperations, lx : _ [] ) = lx |> Array.sumBy getSum
static member inline ($) (SumOperations, a : ^a * ^b ) = match a with a, b -> getSum a + getSum b
static member inline ($) (SumOperations, ox : _ option) = ox |> Option.map getSum |> Option.defaultValue 0
(Some 3, [| 2 ; 1 |]) |> getSum |> printfn "%d" // = 6
답변
런타임 버전은 모든 .net 컬렉션에서 작동합니다. 그러나 AMieres의 런타임 예외에 대한 답변 에서 컴파일러 오류를 교환 하고 AMieres ‘도 36 배 빠릅니다.
let list v = List.replicate 6 v
let rec getSum (input:IEnumerable) =
match input with
| :? IEnumerable<int> as l -> l |> Seq.sum
| e ->
e
|> Seq.cast<IEnumerable> // will runtime exception if not nested IEnumerable Types
|> Seq.sumBy getSum
1 |> list |> list |> list |> list |> list
|> list |> list |> list |> list |> list |> getSum // = 60466176
벤치 마크
| Method | Mean | Error | StdDev |
|---------- |------------:|----------:|----------:|
| WeirdSumC | 76.09 ms | 0.398 ms | 0.373 ms |
| WeirdSumR | 2,779.98 ms | 22.849 ms | 21.373 ms |
// * Legends *
Mean : Arithmetic mean of all measurements
Error : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements
1 ms : 1 Millisecond (0.001 sec)