Help us understand the problem. What is going on with this article?

[Swift]計算結果などに上限値・下限値を設けるサンプルコード

More than 5 years have passed since last update.
  • Xcode 6.2 beta 4
  • Playground

上限値

値が◯◯を超えないようにしたい(=値が◯◯以下になるように抑えたい) とき。

例えば、
「車の速度を加速させるけど、時速 60km は絶対に超えないようにしたい」
...みたいな場合に使用します(良い例が思いつきませんでした)。

学校で習いそうな書き方は以下のようなものでしょうか。

サンプルコード
import UIKit

let upperLimit = 60.0
var speed = 50.0

speed += 50.0 // なんかの計算。ここでは"100.0"となる

if speed > upperLimit {
    speed = upperLimit
}

println(speed) // "60.0"

上記のコードでも全く問題無いのですが、通な(?)プログラマは以下のようにmin関数を用いていると思います。

サンプルコード
import UIKit

let upperLimit = 60.0
var speed = 50.0

speed = min(speed + 50.0, upperLimit) // なんかの計算&上限値込み

println(speed) // "60.0"

比較的短く書ける(値と上限値の登場回数が少なくて済む)ところにメリットがあるのかなと思われます(厳密な所はわかりませんが)。

上限値を超えた時(または上限値に達した時)に値の補正以外に何か別の処理を書きたい場合は前者のコードになると思いますが、単純な計算結果などに上限値を設ける場合は後者の方がよく利用するかなと思います。

若かりし頃、「上限」値を設けるためになんとなく逆の意味っぽい「min」関数を使うという違和感に悩まされましたが、慣れればなんてことはありません。

下限値

値が◯◯より小さくならないようにしたい(=値が◯◯以上になるように抑えたい) とき。

例えば、
「画像のアスペクト比を固定したままサムネイルを作りたい、このとき、画像の横幅と縦幅は 1 より小さくならないようにしたい(1より小さいとマズイ)」
...みたいな場合に使用します。

今度はmax関数を使ってやると楽です。

サンプルコード
import UIKit

let lowerLimit = 1
let scale = 0.001 // 拡大率(適当)
let oldWidth = 10 // 旧横幅(適当)

let newWidth = max(Int(Double(oldWidth) * scale), lowerLimit) // サムネイルの横幅

println(newWidth) // "1"

上限値&下限値

値が◯◯以上△△以下になるように抑えたい とき。

例え話が思いつきません。

min関数とmax関数を組み合わせれば一行で書けます(minとmaxの入れ子の順番はどちらでもよいです)。
値は適当です。

サンプルコード
import UIKit

let lowerLimit = 1
let upperLimit = 100

let test = min(max(34 * 56, lowerLimit), upperLimit) // 34 * 56の計算結果を1以上100以下に抑える

println(test) // "100"

...で、ここからが本題。

この記事を書くきっかけは、そもそもこれと同等の処理が一発で出来る関数はswiftには無いの?という疑問がわいた事でした(ざっと探した限りでは見つけられませんでした)。

他のプログラム言語などではclampという関数が有るようでして、この関数をswiftで書いてみることにしました。
clamp関数の存在自体、Twitterで教えてもらうまで知りませんでした...)

clamp関数の実装
import UIKit

func clamp<T: Comparable>(value: T, lowerLimit: T, upperLimit: T) -> T {
    if value < lowerLimit {
        return lowerLimit
    }
    if value > upperLimit {
        return upperLimit
    }
    return value
}

// テスト
println(clamp(4, 1, 100)) // "4"
println(clamp(-4, 1, 100)) // "1"
println(clamp(400, 1, 100)) // "100"
println(clamp(400.0, 1.0, 100.0)) // "100.0"

ジェネリクスを使っているので、大抵の型で使えると思います。

もし他に良い方法などがあれば教えていただけると幸いです。

takabosoft
仕事でWinかiOS向けのアプリを作っています。 「Dominoの作者」とか「EDGEの作者」とか言われることが多いです。
http://takabosoft.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away