2013-05-16 32 views
11

是否有一些慣用的scala類型將浮點值限制爲由上限或下限定義的給定浮點範圍?數字實際範圍的Scala數據類型

混凝土我想要一個浮動類型,只允許有0.0和1.0之間的值。

更具體的我即將寫一個函數,一個int和這詮釋映射到0.0和1.0之間的範圍內的另一功能,在僞階:

def foo(x : Int, f : (Int => {0.0,...,1.0})) { 
    // .... 
} 

已經搜索的板,但發現什麼都不合適一些隱式魔法或自定義typedef對我來說也是可以的。

+1

你看過[spire](https://github.com/non/spire)嗎? – rsenna

+0

看起來很有希望,會檢查出來! –

回答

8

我不知道如何靜態執行此操作,除了與相關的類型example),Scala沒有。如果只處理常量,應該可以使用執行必要檢查的宏或編譯器插件,但是如果您有任意的浮點型表達式,則很可能需要執行運行時檢查。

這是一種方法。定義執行運行時檢查以確保浮動值在要求的範圍內的一類:

case class NormalisedFloat(val value: Float) 
    extends AbstractRangedFloat(0.0f, 1.0f) 

NormalisedFloat(0.99f) 
NormalisedFloat(-0.1f) // Exception 

或者爲:

case class RangedFloat(val lb: Float, val ub: Float)(val value: Float) 
    extends AbstractRangedFloat(lb, ub) 

val RF = RangedFloat(-0.1f, 0.1f) _ 
RF(0.0f) 
RF(0.2f) // Exception 

abstract class AbstractRangedFloat(lb: Float, ub: Float) { 
    require (lb <= value && value <= ub, s"Requires $lb <= $value <= $ub to hold") 

    def value: Float 
} 

你可以按如下方式使用它

如果可以使用value classes以獲得某些性能,但在構造函數(當前)中調用requires禁止該功能將會很好。


編輯:由@paradigmatic

處理意見這是一個直觀的說法,爲什麼取決於自然數類型可以在一種系統,不(完全)支持依賴類型進行編碼,但範圍內的浮點數可能不能:自然數是一個可枚舉的集合,這使得可以將每個元素編碼爲path-dependent types using Peano numerals。但是,實數不再可以被枚舉,因此不再可能系統地創建與實數的每個元素相對應的類型。

現在,計算機浮點數和實數最終是有限集合,但仍然可以在類型系統中合理高效地枚舉。這組計算機自然數當然也是非常大的,因此對於編碼爲類型的Peano數字的算術提出了一個問題,參見this article的最後一段。然而,我聲稱經常足以與第一個(對於相當小的n)自然數一起工作,例如,由HLists所證明。製作相應的浮動索賠不太令人信服 - 編碼10,000浮點數在0.0到1.0之間,還是在0.0到100.0之間,10,000是更好?

+3

值得注意的是,即使斯卡拉確實有依賴類型可以表達這一點,它會以巨大的代價。想要限制類型的人通常不會想到爲這些類型創造價值的額外負擔。有人給你一個輸入浮點數?確保你有一個測試程序,或者在你的範圍內產生一個浮點數或者告訴你它不適合。對可以顯示結果的浮點數集合執行復雜的數學運算?你需要寫出一個很長的證明(用語言),結果將始終適合該範圍。我個人喜歡這樣做,但很難... –

+0

scala中有依賴類型。看第一個答案:http://stackoverflow.com/questions/12935731/any-reason-why-scala-does-not-explicitly-support-dependent-types – paradigmatic

+0

@paradigmatic曾試圖編碼範圍浮游物(不是nats)與斯卡拉在這方面提供什麼?讓我知道你是否成功。 –

2

下面是將隱式類另一種方法:

object ImplicitMyFloatClassContainer { 

    implicit class MyFloat(val f: Float) { 
    check(f) 

    val checksEnabled = true 

    override def toString: String = { 
     // The "*" is just to show that this method gets called actually 
     f.toString() + "*" 
    } 

    @inline 
    def check(f: Float) { 
     if (checksEnabled) { 
     print(s"Checking $f") 
     assert(0.0 <= f && f <= 1.0, "Out of range") 
     println(" OK") 
     } 
    } 

    @inline 
    def add(f2: Float): MyFloat = { 
     check(f2) 

     val result = f + f2 
     check(result) 

     result 
    } 

    @inline 
    def +(f2: Float): MyFloat = add(f2) 
    } 

} 

object MyFloatDemo { 
    def main(args: Array[String]) { 
    import ImplicitMyFloatClassContainer._ 

    println("= Checked =") 

    val a: MyFloat = 0.3f 
    val b = a + 0.4f 
    println(s"Result 1: $b") 

    val c = 0.3f add 0.5f 
    println("Result 2: " + c) 

    println("= Unchecked =") 

    val x = 0.3f + 0.8f 
    println(x) 

    val f = 0.5f 
    val r = f + 0.3f 
    println(r) 

    println("= Check applied =") 

    try { 
     println(0.3f add 0.9f) 
    } catch { 
     case e: IllegalArgumentException => println("Failed as expected") 
    } 
    } 
} 

它需要一個提示用於編譯器使用隱式類,或者通過顯式地鍵入被加數或通過選擇未提供了一種方法由斯卡拉的浮法。

這樣至少檢查是集中的,所以如果性能是一個問題,您可以關閉它。正如mhs指出的那樣,如果這個類被轉換爲隱式值類,則必須從構造函數中刪除這些檢查。

我已添加@inline註釋,但我不確定,如果這對隱式類有幫助/必要。

最後,我有沒有成功unimport斯卡拉浮動「+」與

import scala.{Float => RealFloat} 
import scala.Predef.{float2Float => _} 
import scala.Predef.{Float2float => _} 

可能還有另一種方式,以推動該編譯器使用隱類

實現這一目標
2

您可以使用值類由MHS爲指出:

case class Prob private(val x: Double) extends AnyVal { 
    def *(that: Prob) = Prob(this.x * that.x) 
    def opposite = Prob(1-x) 
} 

object Prob { 
    def make(x: Double) = 
    if(x >=0 && x <= 1) 
     Prob(x) 
    else 
     throw new RuntimeException("X must be between 0 and 1") 
} 

它們必須使用在同伴對象的工廠方法,這將檢查範圍是正確創建:

scala> val x = Prob.make(0.5) 
x: Prob = Prob(0.5) 

scala> val y = Prob.make(1.1) 
java.lang.RuntimeException: X must be between 0 and 1 

但是使用絕不會產生超出範圍的數值將不需要有效性檢查操作。例如*opposite

+0

我不知道案例類可以擴展AnyVal。涼! –

+0

..因此mhs得到的蜱,無論如何你的很乾淨,謝謝! –

相關問題