2012-02-29 14 views
7

나는 단순한 하스켈 데이터 타입과 함수를 OO로 변환하려고한다. 하지만 혼란 스러워요 ..자바 (OO)에 하스켈 데이터 타입

산술 계산을위한 다음 하스켈 타입 유무 : 이제

data Expr = Lit Int | 
     Add Expr Expr | 
    deriving Show 

--Turn the expr to a nice string 
showExpr :: Expr -> String 
showExpr (Lit n) = show n 
showExpr (Add e1 e2) = "(" ++ showExpr e1 ++ "+" ++ showExpr e2 ++ ")" 

I 변환하려고 해요 ..

public interface Expr { 
    String showExpr(String n); 
} 

// Base case 
public class Lit implements Expr { 
    String n; 

    public Lit(String n) { 
    this.n = n; 
    } 

    @Override 
    public String ShowExpr() { 
    return n; 
    } 
} 

public class Add implements Expr { 
    Expr a; 
    Expr b; 

    public Add(Expr aExpr, Expr bExpr) { 
    this.a = aExpr; 
    this.b = bExpr; 
    } 

    public String ShowExpr() { 
    return "(" + a.ShowExpr() + "+" + b.ShowExpr() + ")"; 
    } 

    public static void main(String[] args) { 
    Lit firstLit = new Lit("2"); 
    Lit secLit = new Lit("3"); 
    Add add = new Add(firstLit,secLit); 
    System.out.println(add.ShowExpr()); 
    } 
} 

이 될 것이다 "(2 + 3) ", 정답입니다.

그러나 ... 나는 이것을 생각하고 OO에서 모델링하는 것이 올바른 방법일까요?

하스켈 데이터 유형을 잘 나타내지 않습니까?

+3

귀하의 전환이 정말 좋다고 생각합니다. 유일한 문제는 Expr이 인터페이스 대신 추상 클래스 여야한다는 것입니다. 그리고 haskell이 훨씬 낫기 때문에 이런 종류의 문제에 자바를 사용하지 마십시오. – nist

답변

5

가능한 한 자세히 코드를 복사 해 봅시다. 여기

는 하스켈 데이터 구조가 일부 속성은 다음과 같습니다

그것은 유형 Expr 두 개의 생성자가
  1. LitAdd
  2. 추가 또는 "외부"에서 생성자를 제거 할 수 없습니다

Java 버전에서 이러한 속성을 유지하려면 다음과 같이해야합니다.

012 이제

showExpr를 변환 : 당신은 Expr 클래스의 정적 방법으로 showExpr을 넣을 수 있습니다

public static String showExpr(final Expr expr) { 
    if(expr.isLit()) { 
     return Integer.toString(expr.asLit().n); 
    } else if(expr.isAdd()) { 
     return "(" + expr.asAdd().a + "+" + expr.asAdd().b + ")"; 
    } 
} 

. 나는 이 아니라이 그것을 인스턴스 메서드로 만들 것입니다. 왜냐하면 그것은 하스켈 버전에서 멀어지기 때문입니다. 각 하스켈 클래스는 자바 인터페이스를되고, instance ...where (또는 derives)는 implements된다 :

+5

'if (e.isLit()) {Lit l = e.asLit(); }'와'if (e instanceof Lit) {Lit l = (Lit) e; }'. Haskell 스타일의 패턴 매칭은 본질적으로 객체 지향 코드가 아니기 때문에'instanceof'와 캐스트를 시뮬레이트하는 메소드로 그 사실을 숨기려하면 코드에 아무런 영향을주지 않습니다. – yshavit

+0

@yshavit, 물론 맞습니다.하지만 제가 제시 한 스타일이 읽기 쉬운 코드로 이어지고, if (foo instanceof Foo) {((Bar) foo) .bla()와 같은 버그가 줄어 듭니다. ; }'리팩토링 때문에 ('Foo! = Bar'). 예를 들어, private 클래스에'Add'와'Lit'를 만들고 싶고, factory 메소드를 사용하고 싶다면,'instanceof'는 작동하지 않을 것입니다. – dflemstr

+0

'e instanceof Lit'가'e.isLit()'보다 다소 읽기 편한지 아닌지는 의견을 남기려합니다. :) 그러나 개인 클래스 인 경우 'e.asLit(). n'도 작동하지 않습니다. – yshavit

2

나는 약간 머리에이 설정됩니다.

그래서, class Show aExpr 데이터 유형은 무엇을 하는가, 이제 다음

interface Showable { 
    String show(); 
} 

이됩니까? Haskell의 여러 생성자 (Java 클래스라고도 함)를 단일 유형으로 결합했으며, 모두 Haskell 클래스 (Java 인터페이스 구현)에 의해 다뤄져 있다고 말합니다. 그래서, 다음 데이터 유형 Expr

interface Expr extends Showable {} 

Lit 생성자되고 말 것, 예를 들어,이된다 : 이제

class Lit implements Expr { 
    @Override 
    String show() { 
     ... 
    } 
} 

당신이 (모두 LitAdd 인스턴스를 포함하는 자바 컬렉션을 가질 수 있습니다 예 : List<Expr>), Expr의 경우 implements Showable을 알 수 있습니다.

Showable을 자신의 인터페이스로 가져 오면 다른 관련없는 Java 클래스도 직접 구현할 수 있습니다. 이것은 여러분이 정의한 하스켈 클래스의 instance ... where을 정의 할 수있는 것과 비슷합니다.

마지막으로 오픈 월드 정책과 클로즈드 월드 정책 사이에는 몇 가지 불일치가 있습니다. Haskell의 instance ... where은 오픈 월드이지만 Java의 implements은 폐쇄되어 있습니다. 그러나 하스켈의 유형별 생성자는 닫혀 있지만, Java의 extends이 대부분입니다.

implements에 대해 할 수있는 일은 많지 않지만 추상적 인 클래스와 개인 생성자를 사용하여 extends을 닫을 수 있습니다. 그렇게하면 해당 동작을 원할 것입니다. 위의 내용을 약간 수정하면 다음과 같이 표시됩니다.

public abstract class Expr implements Showable { 
    private Expr() { 
     // hide the ctor from the outside world, so nobody else 
     // can extend this class 
    } 

    public static class Lit extends Expr { 
     ... 
    } 

    public static class Add extends Expr { 
     ... 
    } 
} 
+0

원본 코드에서 형식 클래스를 사용하는 것이 괜찮 으면 좋겠지 만 단순한 ADT를 모델링 할 때 과도하다고 생각합니다. – Landei

0

코드가 잘 보이지만 좀 더 관용적 일 수 있습니다. final을 사용하여 필드를 불변으로 만들 수 있습니다. toString이 다른 것을하기를 원하지 않는 한, showExptoString (Object으로 선언)으로 바꿀 수도 있습니다. String.format을 사용하면 여러 문자열을 연결하는 것보다 약간 더 깨끗합니다. 마지막으로, 더 작고 유형 안전하므로 Litint을 저장합니다. 여기

내가 그것을 번역 할 방법은 다음과 같습니다

abstract class Exp { 
    // Force subclasses to override Object.toString. 
    public abstract String toString(); 
} 

class Lit extends Exp { 
    final int value; 

    Lit(int value) { 
    this.value = value; 
    } 

    public String toString() { 
    return Integer.toString(value); 
    } 
} 

class Add extends Exp { 
    final Exp a, b; 

    Add(Exp a, Exp b) { 
    this.a = a; 
    this.b = b; 
    } 

    public String toString() { 
    return String.format("(%s + %s)", a, b); 
    } 
} 

class Main { 
    public static void main(String[] args) { 
    Lit firstLit = new Lit(2); 
    Lit secLit = new Lit(3); 
    Add add = new Add(firstLit, secLit); 
    System.out.println(add); 
    } 
} 
1

내가 그런 식으로 번역하는 것 :

public abstract class Expr { 

    public static Expr Lit(final int n) { 
     return new Expr() { 
      public String showExpr() { 
       return "" + n; 
      } 
     }; 
    } 

    public static Expr Add(final Expr e1, final Expr e2) { 
     return new Expr() { 
      public String showExpr() { 
       return String.format("(%s+%s)", e1.showExpr(), e2.showExpr()); 
      } 
     }; 
    } 

    public abstract String showExpr(); 

    public static void main(String[] args) { 
     Expr firstLit = Lit(2); 
     Expr secLit = Lit(3); 
     Expr add = Add(firstLit, secLit); 
     System.out.println(add.showExpr()); 
    } 
} 
하스켈 Lit에서

Add 더 하위 유형이없는을 (이 하스켈 같은 건 없다)하지만, 그냥 Expr 에센스. Java에서 하위 클래스를 노출 할 필요가 없기 때문에 일부 메소드에서 숨겨진 익명 클래스를 사용할 수 있습니다. 단순한 instanceof 테스트가 좀 더 복잡한 예제로 인해 지저분 해 지므로 패턴 매칭 (Java에서 모델링하기가 어렵습니다.)이 필요하지 않는 한 완벽하게 작동합니다.