2016-06-27 58 views
1

我目前正在學習Kotlin並嘗試創建適用於所有number typesByte,Long,Float等)的擴展(中綴)方法。它應該像Python的%操作:如何在Kotlin中爲每個數字類型實現地板模數?

4 % 3 == 1  // only this is the same as Java's % 
4 % -3 == -2 
-4 % 3 == 2 
-4 % -3 == -1 

...或者像Java的Math.floorMod,但也要與DoubleFloat工作:

-4.3 % 3.2 == 2.1000000000000005 

或與這些類型的任何可能的組合

3 % 2.2 == 0.7999999999999998 
3L % 2.2f == 0.7999999999999998 

以下按預期工作,但只適用於兩個Double或兩個Int

inline infix fun Double.fmod(other: Double): Number { 
    return ((this % other) + other) % other 
} 

inline infix fun Int.fmod(other: Int): Number { 
    return ((this % other) + other) % other 
} 

// test 
fun main(args: Array<String>) { 
    println(""" 
      ${-4.3 fmod 3.2} == 2.1000000000000005 

      ${4 fmod 3} == 1 
      ${+4 fmod -3} == -2 
      ${-4 fmod 3} == 2 
      ${-4 fmod -3} == -1 
    """) 
} 

更換IntNumber,我收到以下錯誤信息:

Error:(21, 18) Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: 
@InlineOnly public operator inline fun BigDecimal.mod(other: BigDecimal): BigDecimal defined in kotlin 
Error:(21, 27) Public-API inline function cannot access non-public-API 'internal open fun <ERROR FUNCTION>(): [ERROR : <ERROR FUNCTION RETURN TYPE>] defined in root package' 
Error:(21, 36) Public-API inline function cannot access non-public-API 'internal open fun <ERROR FUNCTION>(): [ERROR : <ERROR FUNCTION RETURN TYPE>] defined in root package' 

我怎樣才能做到這一點對於沒有複製粘貼此每一個數字類型,爲每個類型的組合?

+0

抽象類'Number'沒有成員函數,名爲'mod'也不是'plus'。這可能是錯誤的原因。 – EPadronU

+0

不內聯操作員,它可能會減慢執行速度 – voddan

+0

@voddan由於不需要函數調用,是否存在內聯以提高性能? – Joschua

回答

3

,唯一合理的選擇(也是最快的)是定義運營商要支持每對類型:

infix fun Double.fmod(other: Double) = ((this % other) + other) % other 

infix fun Int.fmod(other: Int) = ((this % other) + other) % other 

infix fun Double.fmod(other: Int) = ((this % other) + other) % other 

infix fun Int.fmod(other: Double) = ((this % other) + other) % other 

這樣的使用是由編譯器進行了哪些類型的決定,而不是在運行時。這些函數不是通用的,也不使用繼承(請參閱Number),這意味着這些值不是裝箱的(請參閱Java原始裝箱),這意味着不分配對象。

我強烈建議不要內聯這些函數。對JVM留下較小的優化。對象未被分配的事實是這裏最大的表現。

P.S函數的數量增長爲支持的類型的平方。你確定你需要支持所有類型?

1

幾分鐘後玩弄的時候,我想出了一個骯髒的方式去做你想要什麼:

import java.math.BigDecimal 
import java.math.BigInteger 

inline infix fun <reified T: Number> T.fmod(other: T): T { 
    return when { 
    this is BigDecimal || other is BigDecimal -> BigDecimal(other.toString()).let { 
     (((BigDecimal(this.toString()) % it) + it) % it) as T 
    } 
    this is BigInteger || other is BigInteger -> BigInteger(other.toString()).let { 
     (((BigInteger(this.toString()) % it) + it) % it) as T 
    } 
    this is Double || other is Double -> other.toDouble().let { 
     (((this.toDouble() % it) + it) % it) as T 
    } 
    this is Float || other is Float -> other.toFloat().let { 
     (((this.toFloat() % it) + it) % it) as T 
    } 
    this is Long || other is Long -> other.toLong().let { 
     (((this.toLong() % it) + it) % it) as T 
    } 
    this is Int || other is Int -> other.toInt().let { 
     (((this.toInt() % it) + it) % it) as T 
    } 
    this is Short || other is Short -> other.toShort().let { 
     (((this.toShort() % it) + it) % it) as T 
    } 
    else -> throw AssertionError() 
    } 
} 

assert(BigDecimal("2.1") == BigDecimal("-4.3") fmod BigDecimal("3.2")) 
assert(BigInteger("2") == BigInteger("-4") fmod BigInteger("3")) 
assert(2 == -4 fmod 3) 
assert(2L == -4L fmod 3L) 

assert(0.7999999999999998 == 3 fmod 2.2) 
assert(0.79999995f == 3L fmod 2.2f) 

我雖然reified將使鑄造(智能和明確的),不必要的,但它並非如此。也許我錯過了一些東西(畢竟我是Kotlin的新手)。

+0

它對'Double'有效嗎? – voddan

+0

@voddan你的意思是混合操作符?如果是這樣的話:不。兩個操作員必須是相同的類型。 – EPadronU

+0

據我所知,這是問題的最初要求 – voddan

0

這裏是沒有任何反映或鑄造一個完全通用的高階功能的方法:

inline fun <T> T.fmod(other: T, mod: T.(T) -> T, plus: T.(T) -> T) = 
    this.mod(other).plus(other).mod(other) 

assert(BigDecimal("2.1") == BigDecimal("-4.3").fmod(BigDecimal("3.2"), BigDecimal::mod, BigDecimal::plus)) 
assert(2L == -4L.fmod(3L, Long::mod, Long::plus)) 

然而,這不是那麼漂亮。

+0

這種情況下沒用,你不覺得嗎? ;) – voddan

相關問題