2013-02-06 40 views
9

क्या संकलन समय पर एक विधि में Vector के आकार को लागू करना संभव है? मैं अंतरिक्ष कि कुछ इस तरह दिखता में अंक का एक संग्रह का उपयोग कर एक n आयामी इयूक्लिडियन स्थान मॉडल करने के लिए (यह क्या मैं अब है) हैं:स्कैला - संकलन समय पर वेक्टर का आकार लागू करना

case class EuclideanPoint(coordinates: Vector[Double]) { 
    def distanceTo(desination: EuclieanPoint): Double = ??? 
} 

अगर मैं एक समन्वय कि EuclideanPoint(Vector(1, 0, 0)) के माध्यम से बनाया जाता है, यह एक 3 डी यूक्लिडियन बिंदु है। यह देखते हुए, मैं यह सुनिश्चित करना चाहता हूं कि distanceTo पर कॉल में पारित गंतव्य बिंदु एक ही आयाम का है।

मैं जानता हूँ कि मैं Tuple22 को Tuple1 उपयोग करके ऐसा कर सकते हैं, लेकिन मैं कई अलग अलग ज्यामितीय रिक्त स्थान का प्रतिनिधित्व करना चाहते हैं और मैं एक अंतरिक्ष के लिए 22 वर्गों लेखन किया जाएगा अगर मैं इसे Tuple रों के साथ किया था - वहाँ एक बेहतर तरीका है?

+1

मैं अच्छे चेतना में यह _answer_ नहीं बना सकता, लेकिन यह एक विचार के रूप में योग्य हो सकता है ... मेरे लिए पहली बात यह है कि पथ-निर्भर प्रकारों के साथ एक मूल्य वर्ग (2.10 में नया) गठबंधन करना है एक प्रकार जो एस विशिष्ट पूर्णांक का प्रतिनिधित्व करता है। मुझे कोई वास्तविक विचार नहीं है कि यह काम करने के लिए किया जा सकता है या नहीं। जब मैं दिन के लिए काम खत्म हो जाता हूं तो मैं इसे आज़मा सकता हूं ... एसआईपी 15 देखें: http://docs.scala-lang.org/overviews/core/value-classes.html –

+0

इस प्रकार की बाधा को एन्कोड किया जा सकता है "टाइप-स्तरीय प्रोग्रामिंग"। उदाहरण के लिए, [Apocalisp ब्लॉग श्रृंखला] देखें (http://apocalisp.wordpress.com/2010/06/08/type-level-programming-in-scala/), और विशेष रूप से एचएलआईस्ट। –

उत्तर

11

यह कई तरीकों से ऐसा करना संभव है कि सभी लोग रैंडल शूलज़ ने एक टिप्पणी में वर्णित किया है।

val orig2d = EuclideanPoint(Sized(0.0, 0.0)) 
val unit2d = EuclideanPoint(Sized(1.0, 1.0)) 

val orig3d = EuclideanPoint(Sized(0.0, 0.0, 0.0)) 
val unit3d = EuclideanPoint(Sized(1.0, 1.0, 1.0)) 

और::

scala> orig2d distanceTo unit2d 
res0: Double = 1.4142135623730951 

scala> orig3d distanceTo unit3d 
res1: Double = 1.7320508075688772 

import shapeless._ 

case class EuclideanPoint[N <: Nat](
    coordinates: Sized[IndexedSeq[Double], N] { type A = Double } 
) { 
    def distanceTo(destination: EuclideanPoint[N]): Double = 
    math.sqrt(
     (this.coordinates zip destination.coordinates).map { 
     case (a, b) => (a - b) * (a - b) 
     }.sum 
    ) 
} 

अब आप निम्नलिखित लिख सकते हैं: Shapeless library एक विशेष रूप से सुविधाजनक कार्यान्वयन है, जो कि इस प्रकार चाहते हैं कि आप कुछ सुंदर के करीब प्राप्त करने देता है प्रदान करता है

लेकिन नहीं:

scala> orig2d distanceTo unit3d 
<console>:15: error: type mismatch; 
found : EuclideanPoint[shapeless.Nat._3] 
required: EuclideanPoint[shapeless.Nat._2] 
       orig2d distanceTo unit3d 
           ^

Sized कई अच्छी सुविधाओं के साथ आता है, जिसमें कुछ हद तक संग्रह संचालन शामिल हैं जो लंबाई के बारे में स्थिर गारंटी के साथ ले जाते हैं। हम उदाहरण के लिए निम्नलिखित लिख सकते हैं:

val somewhere = EuclideanPoint(Sized(0.0) ++ Sized(1.0, 0.0)) 

और त्रि-आयामी अंतरिक्ष में एक सामान्य पुराना बिंदु है।

+0

यह कमाल है, धन्यवाद! त्वरित प्रश्न - क्या 'इंडेस्ड स्क्वायर [डबल]' को 'आकार' संस्करण में बदलना संभव है? मैं कोड के माध्यम से खुदाई कर रहा हूं और इसे समझने में परेशानी हो रही है। – adelbertc

+0

प्रश्न: क्या बेकार को पूर्व-रिलीज/स्नैपशॉट/नवजात स्कैला 2.11 की आवश्यकता होती है? 'क्योंकि जब मैंने इसे पुनर्प्राप्त किया और इसे बनाया, तो उसने 2.11 स्नैपशॉट स्कैला का इस्तेमाल किया। (इसके अलावा, यह संकलित नहीं करता है, लेकिन मुझे पता नहीं है कि इसके बारे में क्या है ...) –

+0

नहीं, आप 2.10.0 (या 2.9.2) के लिए आकारहीन 1.2.3 संस्करण प्राप्त कर सकते हैं, या तो एसबीटी या मेवेन के माध्यम से निर्भरता, या 'आकारहीन-1.2.3' टैग को जांचकर और उस इमारत को देखकर। –

1

प्राकृतिक संख्याओं के प्रकार स्तर एन्कोडिंग करके आप अपने आप को कुछ कर सकते हैं जैसे: http://apocalisp.wordpress.com/2010/06/08/type-level-programming-in-scala/। फिर बस एक प्राकृतिक द्वारा अपने वेक्टर parametrizing। अतिरिक्त निर्भरता की आवश्यकता नहीं होगी, लेकिन शायद बेकार का उपयोग करके अधिक जटिल हो जाएगा।