2013-05-16 30 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得到的蜱,无论如何你的很干净,谢谢! –

相关问题