2016-06-26 16 views
1

私はScalaを初めて使いました。可能な限り最も基本的な知識でこれを表現するより良い方法がありますか?リストの最大値をスカラーで再帰的に見つける

def findMax(xs: List[Int]): Int = { 
     xs match { 
     case x :: tail => (if (tail.length==0) x else (if(x>findMax(tail)) x else (findMax(tail)))) 
     } 
    } 
+0

空リストを取得する場合は、Option(Int)を返す必要があります。 –

答えて

7

ここでは2つの問題があります。まずを呼び出します。これはO(N)の操作です。最悪の場合、これはN * N個のステップを必要とします。ここで、Nはシーケンスの長さです。もう1つは、関数が末尾再帰的ではないということです。findMaxを「外側から内側に」呼び出すことができます。

正しい再帰関数を記述するために通常の戦略は、それぞれの可能なパターンの場合について考えて

  • である:ここでは、空のリストNilまたは非空のリストhead :: tailのいずれかを持っています。これはあなたの最初の問題を解決します。
  • この関数の別の引数として、一時的な結果(ここでは最大値の現在の推測)を持ちます。これはあなたの2番目の問題を解決します。

これは与える:

import scala.annotation.tailrec 

@tailrec 
def findMax(xs: List[Int], max: Int): Int = xs match { 
    case head :: tail => findMax(tail, if (head > max) head else max) 
    case Nil => max 
} 

val z = util.Random.shuffle(1 to 100 toList) 
assert(findMax(z, Int.MinValue) == 100) 

あなたは、この追加の引数を公開したくない場合は、補助内部関数を書くことができます。

def findMax(xs: List[Int]): Int = { 
    @tailrec 
    def loop(ys: List[Int], max: Int): Int = ys match { 
    case head :: tail => loop(tail, if (head > max) head else max) 
    case Nil => max 
    } 
    loop(xs, Int.MinValue) 
} 

val z = util.Random.shuffle(1 to 100 toList) 
assert(findMax(z) == 100) 

簡略化のため、リストが空の場合はInt.MinValueを返します。この場合、例外をスローする方がよい場合があります。


ここ@tailrec注釈は、それは単に私たちが実際に末尾再帰関数を定義していることを保証し、オプションです。これは、リストが極端に長い場合にスタックオーバーフローを生成できないという利点があります。

+0

空のリストは、Int.MinValueを生成します。これは、空でないInt.MinValuesのリストと同じです。 –

+0

@userunknownはい、これは任意の選択です。 'if(xs.isEmpty)は新しいUnsupportedOperationException(" findMax(empty) ")'を投げることができます。三方向のパターンマッチについては、@ elmの答えも見てください。 –

5

コレクションを1つの値に減らす場合は、明示的な再帰の代わりにfoldのいずれかの関数を使用することを検討してください。再帰パターンマッチングを使用

List(3,7,1).fold(Int.MinValue)(Math.max) 
// 7 
+1

ええ、これは良いアプローチですが、私はOPが再帰について学習しようとしていると思います。また、 'reduce(_ max _)'を使うと 'MinValue'は必要ありません。 – jwvh

+0

'reduce'はOPの元のコードと同じ問題を抱えていますが、あなたは部分的な関数になります。 –

+0

それから、 'reduceOption'を使うことができます。それは、コレクションが空であるかどうかを知りたいのか、またはリストかどうかにかかわらず、整数を持つだけであるかどうかによって異なります。 –

0

def top(xs: List[Int]): Int = xs match { 
    case Nil  => sys.error("no max in empty list") 
    case x :: Nil => x 
    case x :: xs => math.max(x, top(xs)) 
} 

パターンマッチングは、ヘッド、残りにリストを分解するために使用されます。 1つの要素リストはx :: Nilと表示されます。リストの残りの部分について再帰的に反復し、各再帰的段階でリストの先頭項目の最大値を比較します。ケースを網羅的にするため(明確な関数を作るために)、空リスト(Nil)も考慮します。

0
def maxl(xl: List[Int]): Int = { 
    if ((xl.head > xl.tail.head) && (xl.tail.length >= 1)) 
    return xl.head 
    else 
    if(xl.tail.length == 1) 
     xl.tail.head 
    else 
     maxl(xl.tail) 
} 
+3

こんにちはRahul、ソリューションを説明しようとすることができますし、他の答えよりも優れている理由は何ですか?この回答がアップ投票に値するかどうかは、他の人が判断するのに役立ちます。 –

+0

@VlastimilOvčáčík私は自分のアプローチが他のものより優れているとは思わないが、私が与えたレスポンスはScalaの私の最初の段階に基づいていた(私はまだそれを学ぼうとしている)。今のところ私はChris Martinの答えが好きです。これはList(3,7,1).fold(Int.MinValue)(Math.max)です。 –

関連する問題