2013-10-13 2 views
0

Clojure에서 몇 가지 기본 물리/화학 수식을 구현하고 싶습니다. 퍼포먼스가 아니라 편리함을 강조하고 싶으므로 주 기능은 유형 검사입니다. 나는 메타에 숫자 을 붙이는 것이 그 일을 성취 할 것이라고 생각했다. 예를 들어Clojure : 메타를 지원하는 최소 구현?

,이 기능 :

(defn force [mass accel] 
    (* mass accel)) 

한다

  1. 액세스
  2. 확인이 등 대량 유형, 즉 kg, g의의 확인 오류가있는 경우를 던져 첫 번째 인수의 메타 그렇지 않습니다.
  3. 숫자 값을 킬로그램으로 변환하십시오.
  4. 가속도를 같게하십시오.
  5. 뉴턴의 메타로 결과를 반환합니다.

내 네임 스페이스에 * 및 기타 기능을 적절하게 오버로드 할 수 있습니다.

유일한 문제는 Double에 메타를 첨부 할 수 없다는 것입니다. 숫자처럼 작동하지만 메타 데이터를 가질 수있는 무언가를 얻는 좋은 방법은 무엇입니까?

+0

'java.lang.Long' 및 친구들이'final'이므로'java.math.BigDecimal' 옵션을 사용하고 있습니까? 그렇다면 그것을 확장하고'clojure.lang.IObj'를 구현할 수 있습니다. –

+0

어떤 방법으로 확장해야합니까? deftype/gen-class/reify? 작은 모범을 보여 주실 수 있습니까? –

+0

방금 ​​일부 코드와 함께 답변을 추가했습니다. 나는 그것을 구현하는 방법에 대해 궁금해서 정말 작은 예제는 아닙니다. P. 희망이 도움이됩니다. –

답변

1

여기에 gen-class을 사용하는 방법이 있습니다. 나는 단위를 확인하고 정규화하기위한 함수를 조롱했다. 구현 된 유일한 작업은 *이며 force에서 사용됩니다. 코드가 당신은 당신의 leiningen 프로젝트 폴더의 src 폴더에 big_decimal_meta.clj라는 파일에 다음 코드를 저장하고로드해야합니다 compilegen-class을 사용하고 있기 때문에

주의하시기 바랍니다.

BigDecimalMetagen-class를 사용하여 :

(ns big-decimal-meta 
    (:refer-clojure :exclude [* force]) 
    (:gen-class 
    :name  BigDecimalMeta 
    :extends  java.math.BigDecimal 
    :state  metadata 
    :init  init 
    :implements [clojure.lang.IObj])) 

(defn -init [& args] 
    [args (atom nil)]) 

(defn -withMeta [this metadata] 
    (reset! (.metadata this) metadata) 
    this) 

(defn -meta [this] 
    (deref (.metadata this))) 

(compile 'big-decimal-meta) 

*force 기능을 몇 가지 예제 코드 :

(def x (with-meta (BigDecimalMeta. 1) {:unit :kg})) 
(def y (with-meta (BigDecimalMeta. 3.5) {:unit :mss})) 
(def z (with-meta (BigDecimalMeta. 4.5) {:unit :V})) 

(defn unit [x] 
    (-> x meta :unit)) 

(defn * [x y] 
    (BigDecimalMeta. (str (.multiply x y)))) 

(defn mass? [x] 
    (#{:kg :gr :mg ,,,} (unit x))) 

(defn accel? [x] 
    (#{:mss ,,,} (unit x))) 

(defn to-kg [x] x) 
(defn to-mss [x] x) 

(defn force [mass accel] 
    (assert (mass? mass)) 
    (assert (accel? accel)) 
    (let [mass (to-kg mass) 
     accel (to-mss accel)] 
    (with-meta (* mass accel) {:unit :N}))) 

(println (force x y) (meta (force x y))) 
(println (force x z) (meta (force x z))) 
+0

아주 훌륭하게 작동합니다. –

2

이 모든 당신은 단지 숫자와 단위를 모두 포함지도를 만들 경우 훨씬 더 쉽게, 숫자의 메타 데이터의 일부로 유닛을 밀입국시키기보다는 결국, 단위는 개념적으로 숫자에 대한 데이터를 부기하지 않습니다. 이는 수행중인 계산의 필수적인 부분입니다. 유닛을 무시하면서 번호를 사용할 수있는 것처럼 아니기 때문에 장식 번호를 +과 같은 일부 "벙어리 (dumb)"단위 인식 기능에 전달하는 기능은 흥미롭지 않습니다. 당신의 to-kgto-mss 기능은 유형 자체를 확인하는 경우

(defn force [{munit :unit :as mass} {aunit :unit :as accel}] 
    (assert (mass? munit)) 
    (assert (accel? aunit)) 
    {:unit :newton, :magnitude (* (:magnitude (to-kg mass)) 
           (:magnitude (to-mss accel)))}) 

그리고 물론

, 당신은 force에이를 생략 할 수 있습니다 :

는이 모든 것을 감안할 때, 그것은 당신의 force 예 기능을 구현하기 쉽습니다. 지도에 메타 데이터가 포함 된 숫자가 있다는 상상의 편의를 위해지도의 단순성과 투명성을 포기하지 마십시오.

+0

2 배의 힘이 여전히 강합니다. 데코레이션 된 숫자를 사용하면 평범한 기능이 많은 에서 사용할 수 있습니다. 그리고 과부하의 요지는 가능한 한 간단하게 함수에 단위 체크를 추가하는 DSL을 만드는 것입니다 (이상적으로는 (defn force^: Newton [^ : kg mass^: mss accel] (* mass accel))'). –

관련 문제