2010-07-08 4 views
3

특정 유형 또는 해당 하위 유형을 인수 중 하나로 받아들이는 함수를 작성하려고 시도하고 유형 또는 해당 하위 유형의 값을 리턴합니다.유연한 형식 인수 및 반환 값이있는 함수?

[<AbstractClass>] 
type Spreader() = 
    abstract Fn : unit -> unit 

type Fire() = 
    inherit Spreader() 
    override self.Fn() =() 

type Disease() = 
    inherit Spreader() 
    override self.Fn() =() 

let spread (spr:#Spreader) : #Spreader = 
    match spr with 
    | :? Fire -> Fire() 
    | :? Disease -> Disease() 
    | _ -> failwith "I don't get it" 

분명히 이것은 작동하지 않지만 내가하려는 일을하게됩니다.

처음에는 Spreader 유형에서 추상 함수를 구현하고 하위 유형에서이를 오버라이드했으나이를 피하기 위해 업 캐스팅이 필요했습니다.

이 것이 가능합니까? 제네릭을 살펴보고 있지만 F # 구현에 대해 아직 이해하지 못했습니다. 나는 차별 노동 조합을 사용하는 제안에 대해서는 편집 2010년 7월 8일 태평양 표준시 17시 30분

, 내가 그 전에 시도 것입니다. 문제는 내가 기본 유형의 멤버로 정의한 모든 함수가 분기를 처리해야한다는 것입니다. 예를 들어 : 아무 의미가없는, 너무,

type strength = float32 

type Spreader = 
    | Fire of strength 
    | Disease of strength 

    member self.Spread() = 
     match self with 
     | Fire str -> Fire str 
     | Disease str -> Disease str 

    member self.Burn() = 
     match self with 
     | Fire str -> Fire str 
     | _ -> failwith "Only fire burns" 

확산 기능은 여기에 잘 작동하지만 난 화재 굽기 싶은 경우에, 나는 질병을 제공해야합니다. 예를 들어,

나는 Disease.Burn하려고 같은 불법적 인 일을 할 수있는 사용자가 가능한 시도에 허용 할, 그러나 나는 사방에 옵션 유형의 무리를 반환하고 싶지 않습니다

member self.Burn() = 
    match self with 
    | Fire str -> Some(Fire str) 
    | _ -> None 

저는 Burn 기능을 Fire Spreader에만 엄격하게 적용하고 질병 확산기에 대해서는 정의하지 않았습니다.

또한 속성에 대해서도 마찬가지입니다. 불에 질병이 있다는 것을 이해시키지 못하게하고, 그 반대의 경우도 있습니다. 또한 점 표기법을 사용하여 스프레드 어의 강도 값에 액세스 할 수 있기를 바란다. 어쨌든 내가 그런 식으로 액세스하는 다른 멤버를 가지게 될 것이므로, 멤버를 정의해야하는 것은 이상하게 보일 수도있다. 객체에 캡슐화됩니다. (예 : 질병의 str -> ...)

또 다른 옵션은 스프레더 유형에서 스프레드 및 굽기 기능을 간단히 분리하는 것입니다. 그런 다음 (1) 추악한 업 캐스팅이나 제네릭 코드 (2) SpreadFire와 SpreadDisease라는 이름을 지어야했기 때문에 (SpreadFire와 SpreadDisease와 같은) Spread의 경우를 빨아들이는 Fire and Disease를위한 함수를 완전히 분리했다. (함수형이 기묘하게 오버로딩되기 때문에 허용되지 않음).

F #의 멍청한 놈으로, 나는 모든 비판과 제안을 환영한다. :)

편집 2010년 7월 9일 태평양 표준시 08시 45분

존 Harrop에 "왜 당신이 사용하는 보강 및 회원?"

실제 코드의 형식이 수학 집중식이기 때문에 초기화시 특정 값을 사전 계산하고 멤버로 저장합니다.

Jon Harrop : "왜 Speader 유형에만 적용 할 수 있는지 레코딩하고 싶습니까?"스프레더의 구성원으로 기능을 구현하고, 분리하는 사이에, 그러나, 나는이 찢어졌다. 나는 그것이 전적으로 화재에 적용 할. 스프레더에 적용 할 굽기 원하지 내가 쓴 것처럼

, 내가 할 스프레더 유형의 IT (불일치 냄새.)

존 Harrop에가 :. "당신의 FN 구성원의 목적은 무엇인가?"

죄송합니다, 그건 그냥 외부 코드가 무시하시기 바랍니다했다

.

Jon Harrop : "왜 인터페이스 대신 추상 클래스를 사용 했습니까?"

대부분 코드 가독성 및 효율성. 그 인터페이스 중 하나를 사용하기 위해 인터페이스에 업 캐스트해야하는 것이 싫다.

Jon Harrop : "왜 강점을 float32의 별칭으로 정의 했습니까?"

코드 가독성.

Jon Harrop : "당신이 해결하려고하는 실제 구체적인 문제는 무엇입니까?!"

가독성을 유지하면서 관련 유형에 대한 코드 공유.

흥미롭게도 당신이 제안한 솔루션은 내가 처음 시도한 솔루션입니다 (저는이 F # 일을 1 주일 정도만 했었습니다).하지만 그 솔루션을 버렸습니다. 타입 정의 밖에서 정의 된 함수를 오버로드 할 수 없기 때문에 DU에 내 기본 유형을 랩핑합니다. 나는 근본적인 유형의 잘못된 발로 빠져 나가는 것에 대해 편집증 적이다. (부분적으로는 VS2010에서 F # 리팩터링이 부족하다는 이유로). 함수 프로그래밍은 지금 당장 나에게 큰 관심사가되는 주제이며, 나는 이해를 위해 낚시를하고있다.

편집 2010년 7월 9일 태평양 표준시 22시 30분

사실, I/정말 http://briancarper.net/blog/315/functional-programming-hurts-me에서 저자 동의 (함수형 프로그래밍을 좋아하지 않는 시작 해요 - 내가 한 번 더 피보나치가 표시되는 경우 또는 F #으로 코딩 예를 팩토리얼 튜토리얼 또는 교과서, 내가 찾은 다음 얼간이를 찌를거야).

Jon이 전시 한 경멸 (아래)에서 내가 여기있는 누군가가 내 최신 응답 (특정 작업을하는 이유 설명)에서 다시 쏠 수 있기를 바랬습니다. 나는 거만한 엘리트들로부터 FP를 배우기를 원합니다. 그들의 코담배들은 그들의 sh * t가 악취가 나지 않는다고 생각합니다.

+0

것은 내가 돈 '브라이언의 존에서 솔루션 및 spread2 달리


이 버전은 위의 다음과 같은 일을해야한다는 것을 의미는 인수로 얻는 하나와 같은 하위 유형을 반환 Jon, Brian, Tomas가 이미 가지고있는 것보다 더 말할 것도 없지만, 그 가치는 무엇인가를 위해, 언어를 해킹하고 해킹하여 무언가를 작동 시키면 거의 항상 잘못된 것을하고 있음을 의미합니다. 이 경우 상속은 C#에서 일반적으로 사용되는 관용구가 아니므로 클래스 대신 일치 할 수있는 공용체처럼 관용적 인 무언가를 사용하고 싶을 것입니다.실망하지 마십시오. 거의 모든 사람들이 F # 구문을 사용하여 처음 C#을 작성합니다 (http://stackoverflow.com/questions/1883246/). 숙어를 익히면 언어를 쉽게 사용할 수 있습니다. – Juliet

+0

조언 주셔서 감사합니다, 줄리엣. 나는 당신의 훈계를 염두에 두겠다. 당분간, 나는 Jon의 제안 (아이러니하게도이 프로젝트에서 가장 먼저 수행 한 솔루션 임)을 제시했다. – MiloDC

답변

4

당신은 아마 명시 적으로 반환 값을 업 캐스팅해야합니다

let spread (spr: Spreader) = 
    match spr with 
    | :? Fire -> (Fire() :> Spreader) 
    | :? Disease -> (Disease() :> Spreader) 

이것은 매우 특이한 프로그래밍 스타일이 생각입니다. 너 정확히 뭘 하려구?

편집

난 아직도 당신이 달성 하려는지 이해가 안 돼요. 왜 증원과 회원을 사용하고 있습니까? burnSpeader 유형에만 적용하려는 이유는 무엇입니까? Fn 회원의 목적은 무엇입니까? 왜 인터페이스 대신 추상 클래스를 사용 했습니까? float32의 별칭으로 strength을 정의한 이유는 무엇입니까? 당신이 해결하려고하는 실제 구체적인 문제는 무엇입니까? 이것에 대해

무엇 : 당신은 뿌리의 부분 집합에 적용되는 기능이있는 경우

type fire = ... 

let burn fire = ... 

type spreader = Disease | Fire of fire 

let spread = function 
    | Disease -> ... 
    | Fire fire -> ... 

기본적으로, 당신은 어떻게 든 문제를 분리해야합니다. 위와 같이 새로운 유형을 통해 간접적으로 작업하거나 OOP를 사용하고 클래스의 하위 집합을 인터페이스로 구현합니다.

+0

"정확히 무엇을하려고합니까?" 위의 내용을 참조하십시오. – MiloDC

+0

그에 따라 답변을 업데이트했습니다. –

+0

업데이트에 응답했습니다. – MiloDC

2

아래에서 spread1 또는 spread2 중 하나를 수행 할 수 있지만 클래스 계층 구조가 아닌 구분 된 공용 구조체를 사용할지 궁금합니다. 당신이 클래스 계층 구조를 원하는 것처럼

[<AbstractClass>] 
type Spreader() = 
    abstract Fn : unit -> unit 

type Fire() = 
    inherit Spreader() 
    override self.Fn() =() 

type Disease() = 
    inherit Spreader() 
    override self.Fn() =() 

let fire = new Fire() 
let disease = new Disease() 

let spread1<'t when 't :> Spreader>(spr:'t) : 't = 
    spr 

printfn "%A" (spread1 fire) 
printfn "%A" (spread1 disease) 

let spread2(spr:Spreader) : Spreader = 
    match spr with 
    | :? Fire -> upcast disease 
    | :? Disease -> upcast fire 
    | _ -> failwith "hmmm" 

printfn "%A" (spread2 fire) 
printfn "%A" (spread2 disease) 

편집하여 업데이트 한 후

, 그것은 소리, 당신은 정확하게 당신이 예에서 그들이 할 것 같은 일을 할 것입니다 기음#. 그래서 그렇게 할거야. 현재

let spread (spr:Spreader) : Spreader = 
    match spr with 
    | :? Fire -> upcast Fire() 
    | :? Disease -> upcast Disease() 
    | _ -> failwith "I don't get it" 

은 제대로 작동하며 원본과 비슷합니다.그래도 Spread을 기본 클래스의 추상 메소드로 만들 수 있습니다.

F #에서 "유연한 형식"(#type)이 거의 필요하지 않으므로 필요할 경우 코드 냄새가 날 때가 많습니다.

4

F #에서는 사람들이 종종 구현 상속 (기본 클래스)을 사용하지 않으므로 수행하려는 것을 아는 것이 유용합니다 (예 :보다 현실적인 예가 좋습니다).

올바르게 이해하면 spread 함수는 Spreader 유형의 하위 유형을 가져와 같은 유형의 값을 반환해야합니다. 이 같은 함수의 서명을 작성할 수 있습니다

let foo< 'T when 'T :> Spreader >(arg:'T) : 'T = 
    // some implementation here 

이 유형은 매개 변수 'TSpreader과 유형 서명 쇼와 같은 일부 상속 클래스는 인수로 취와 같은 형식을 반환 foo 기능이 될 것입니다. 차별 노동 조합이 더 나은 선택이 될 것입니다 경우

let foo< 'T when 'T :> Spreader >(arg:'T) : 'T = 
    match box arg with 
    | :? Fire -> (box (Fire())) :?> 'T 
    | :? Disease -> (box (Disease())) :?> 'T 

을 어쨌든, 나는이 시나리오처럼 보인다 브라이언 동의 : 동적 유형의 많은 캐스트를 추가해야하기 때문에

구현은, 조금 못생긴 . 문제를 좀 더 자세히 설명하면 좋을 것입니다.

let fire = new Fire() 
let fire2 = foo fire  // 'fire2' has type 'Fire' 
fire2.FireSpecificThing() 
+0

"... 이것은 차별 노동 조합이 더 나은 선택이 될 수있는 시나리오처럼 보입니다. 문제를 더 자세히 설명하면 좋을 것입니다." 위의 내용을 참조하십시오. – MiloDC

+0

@MiloDC : 설명해 주셔서 고맙습니다. 그래서 여러분은 불과 질병을 나타 내기위한 두 개의 실체를 가지며, 그들 모두는 힘을 가질 수 있습니다. 이러한 유형의 다른 유형을 쉽게 추가 하시겠습니까? 어떻게 그들을 사용하려고합니까? 당신이 당신의 불의 실체에 '불'멤버가 있다면, 그 불은 어디에서 왔을까요? 기본 클래스를 사용하여 이러한 작업을 항상 수행하지 않겠습니까? 예를 들어 게임과 같은 것입니까? ... 이들은 OO (클래스 계층 구조) 또는 FP 스타일 (차별화 된 유니온)을 사용할지 여부를 결정하는 데 사용할 수있는 수준 높은 질문입니다. –

+0

Tomas, 나는 최근에 당신의 책 _Real World Functional Programming_을 구입했다고 말하고 싶습니다. 그리고 지금까지는 아주 잘 쓰여진 것 같습니다. 나는 당신의 책에서 읽은 것에 크게 근거하여 나의 문제를 다르게 생각할 것입니다. 건배. – MiloDC