IT-Swarm.Net

Làm thế nào để tìm phần tử lớn nhất trong danh sách các số nguyên đệ quy?

Tôi đang cố gắng viết một hàm sẽ tìm đệ quy phần tử lớn nhất trong danh sách các số nguyên. Tôi biết cách thực hiện điều này trong Java, nhưng không thể hiểu cách thực hiện điều này tại Scala.

Đây là những gì tôi có cho đến nay, nhưng không có đệ quy: 

  def max(xs: List[Int]): Int = {
    if (xs.isEmpty) throw new Java.util.NoSuchElementException();
    else xs.max;
  }

Làm thế nào chúng ta có thể tìm thấy nó một cách đệ quy với ngữ nghĩa Scala.

15
nazar_art

Đây là cách triển khai đệ quy tối thiểu nhất mà tôi từng có thể nghĩ ra:

def max(xs: List[Int]): Option[Int] = xs match {
  case Nil => None
  case List(x: Int) => Some(x)
  case x :: y :: rest => max( (if (x > y) x else y) :: rest )
} 

Nó hoạt động bằng cách so sánh hai yếu tố đầu tiên trong danh sách, loại bỏ yếu tố nhỏ hơn (hoặc yếu tố đầu tiên, nếu cả hai đều bằng nhau) và sau đó tự gọi nó trong danh sách còn lại. Cuối cùng, điều này sẽ giảm danh sách thành một yếu tố phải lớn nhất.

Tôi trả lại Tùy chọn để xử lý trường hợp được cung cấp danh sách trống mà không đưa ra ngoại lệ - điều này buộc mã cuộc gọi phải nhận ra khả năng và xử lý nó (tùy thuộc vào người gọi nếu họ muốn ném ngoại lệ) .

Nếu bạn muốn nó chung chung hơn, nó nên được viết như thế này:

def max[A <% Ordered[A]](xs: List[A]): Option[A] = xs match {
  case Nil => None
  case x :: Nil => Some(x)
  case x :: y :: rest => max( (if (x > y) x else y) :: rest )
}

Cái nào sẽ hoạt động với bất kỳ loại nào mở rộng đặc điểm Ordered hoặc trong đó có một chuyển đổi ngầm định từ A thành Ordered[A] trong phạm vi. Vì vậy, theo mặc định, nó hoạt động cho Int, BigInt, Char, String, v.v., bởi vì scala.Predef xác định chuyển đổi cho chúng.

Chúng ta có thể trở nên chung chung hơn như thế này:

def max[A <% Ordered[A]](xs: Seq[A]): Option[A] = xs match {
  case s if s.isEmpty || !s.hasDefiniteSize => None
  case s if s.size == 1 => Some(s(0))
  case s if s(0) <= s(1) => max(s drop 1)
  case s => max((s drop 1).updated(0, s(0)))
}

Cái nào sẽ hoạt động không chỉ với danh sách mà cả vectơ và bất kỳ bộ sưu tập nào mở rộng đặc điểm Seq. Lưu ý rằng tôi đã phải thêm một kiểm tra để xem chuỗi có thực sự có kích thước xác định hay không - đó có thể là một luồng vô hạn, vì vậy chúng tôi quay lại nếu đó có thể là trường hợp. Nếu bạn chắc chắn luồng của mình sẽ có kích thước xác định, bạn luôn có thể buộc nó trước khi gọi chức năng này - dù sao nó cũng sẽ hoạt động trong toàn bộ luồng. Xem ghi chú ở cuối để biết lý do tại sao tôi thực sự sẽ không muốn trả lại None cho một luồng không xác định. Tôi đang làm nó ở đây hoàn toàn đơn giản.

Nhưng điều này không làm việc cho các bộ và bản đồ. Phải làm sao Siêu kiểu phổ biến tiếp theo là Iterable, nhưng điều đó không hỗ trợ updated hoặc bất cứ thứ gì tương đương. Bất cứ điều gì chúng tôi xây dựng có thể được thực hiện rất kém cho loại thực tế. Vì vậy, đệ quy không có chức năng trợ giúp sạch của tôi bị phá vỡ. Chúng tôi có thể thay đổi sử dụng hàm trợ giúp nhưng có rất nhiều ví dụ trong các câu trả lời khác và tôi sẽ tiếp tục sử dụng phương pháp một chức năng đơn giản. Vì vậy, tại thời điểm này, chúng ta có thể chuyển sang reduceLeft (và trong khi chúng ta đang ở đó, chúng ta hãy đi 'Traversable' và phục vụ cho tất cả bộ sưu tập):

def max[A <% Ordered[A]](xs: Traversable[A]): Option[A] = {
  if (xs.hasDefiniteSize) 
    xs reduceLeftOption({(b, a) => if (a >= b) a else b}) 
  else None
}

nhưng nếu bạn không xem xét đệ quy lessLeft, chúng ta có thể làm điều này:

def max[A <% Ordered[A]](xs: Traversable[A]): Option[A] = xs match {
  case i if i.isEmpty => None
  case i if i.size == 1 => Some(i.head)
  case i if (i collect { case x if x > i.head => x }).isEmpty => Some(i.head)
  case _ => max(xs collect { case x if x > xs.head => x })
}

Nó sử dụng bộ kết hợp collect để tránh một số phương thức vụng về để tránh một Iterator mới ra khỏi xs.headxs drop 2.

Một trong hai thứ này sẽ hoạt động an toàn với hầu hết mọi bộ sưu tập bất cứ thứ gì có đơn đặt hàng. Ví dụ:

scala>  max(Map(1 -> "two", 3 -> "Nine", 8 -> "carrot"))
res1: Option[(Int, String)] = Some((8,carrot))

scala> max("Supercalifragilisticexpialidocious")
res2: Option[Char] = Some(x)

Tôi thường không lấy những thứ này làm ví dụ, vì nó đòi hỏi kiến ​​thức chuyên môn hơn về Scala.

Ngoài ra, hãy nhớ rằng đặc điểm Traversable cơ bản cung cấp phương thức max, vì vậy tất cả chỉ dành cho thực hành;)

Lưu ý: Tôi hy vọng rằng tất cả các ví dụ của tôi cho thấy sự lựa chọn cẩn thận của chuỗi các biểu thức trường hợp của bạn có thể làm cho mỗi biểu thức trường hợp riêng lẻ càng đơn giản càng tốt.

Lưu ý quan trọng hơn: Ồ, đồng thời, tôi rất thoải mái khi trả lại None cho đầu vào của Nil, trong thực tế, tôi rất có xu hướng ném ngoại lệ cho hasDefiniteSize == false. Đầu tiên, một luồng hữu hạn có thể có kích thước xác định hoặc không xác định phụ thuộc hoàn toàn vào chuỗi đánh giá và chức năng này sẽ trả về ngẫu nhiên Option trong những trường hợp đó - có thể mất nhiều thời gian để theo dõi. Thứ hai, tôi muốn mọi người có thể phân biệt giữa việc đã vượt qua Nil và đã vượt qua đầu vào rủi ro thực sự (nghĩa là một luồng vô hạn). Tôi chỉ trả về Option trong các cuộc biểu tình này để giữ mã đơn giản nhất có thể.

34
itsbruce

Lưu trữ đám mây nhanh chóng, đáng tin cậy và giá cả phải chăng

Đăng ký và nhận $50 tiền thưởng trong vòng 30 ngày!

Cách tiếp cận đơn giản nhất là sử dụng chức năng tối đa của tính trạng TraversableOnce, như sau,

val list = (1 to 10).toList
list.max

để bảo vệ chống lại sự trống rỗng, bạn có thể làm một cái gì đó như thế này,

if(list.empty) None else Some(list.max)

Ở trên sẽ cung cấp cho bạn một Option[Int]

Cách tiếp cận thứ hai của tôi sẽ là sử dụng foldLeft

(list foldLeft None)((o, i) => o.fold(Some(i))(j => Some(Math.max(i, j))))

hoặc nếu bạn biết một giá trị mặc định sẽ được trả về trong trường hợp danh sách trống, việc này sẽ trở nên đơn giản hơn.

val default = 0
(list foldLeft default)(Math.max)

Dù sao vì yêu cầu của bạn là thực hiện theo cách đệ quy, tôi đề xuất như sau,

def recur(list:List[Int], i:Option[Int] = None):Option[Int] = list match {
  case Nil => i
  case x :: xs => recur(xs, i.fold(Some(x))(j => Some(Math.max(j, x))))
}

hoặc như trường hợp mặc định,

val default = 0
def recur(list:List[Int], i:Int = default):Int = list match {
  case Nil => i
  case x :: xs => recur(xs, i.fold(x)(j => Math.max(j, x)))
}

Lưu ý rằng, đây là tail recursive. Do đó ngăn xếp cũng được lưu.

16
tiran

Nếu bạn muốn tiếp cận chức năng cho vấn đề này thì hãy sử dụng reduceLeft:

def max(xs: List[Int]) = {
  if (xs.isEmpty) throw new NoSuchElementException
  xs.reduceLeft((x, y) => if (x > y) x else y)
}

Hàm này dành riêng cho danh sách các số nguyên, nếu bạn cần cách tiếp cận tổng quát hơn thì hãy sử dụng kiểu chữ Ordering:

def max[A](xs: List[A])(implicit cmp: Ordering[A]): A = {
  if (xs.isEmpty) throw new NoSuchElementException
  xs.reduceLeft((x, y) => if (cmp.gteq(x, y)) x else y)
}   

reduceLeft là một hàm bậc cao hơn, có hàm kiểu (A, A) => A, trong trường hợp này, nó cần hai số nguyên, so sánh chúng và trả về hàm lớn hơn.

13
4lex1v

Bạn có thể sử dụng khớp mẫu như thế

def max(xs: List[Int]): Int = xs match {
  case Nil => throw new NoSuchElementException("The list is empty")
  case x :: Nil => x
  case x :: tail => x.max(max(tail)) //x.max is Integer's class method
}
8
Nam Vo

Scala là một ngôn ngữ chức năng, theo đó người ta được khuyến khích suy nghĩ đệ quy. Giải pháp của tôi như dưới đây. Tôi tái diễn nó dựa trên phương pháp đã cho của bạn.

  def max(xs: List[Int]): Int = {
    if(xs.isEmpty == true) 0
    else{
      val maxVal= max(xs.tail)
      if(maxVal >= xs.head) maxVal 
      else                  xs.head     
    }
  }

Cập nhật giải pháp của tôi để đuôi đệ quy nhờ các đề xuất.

  def max(xs: List[Int]): Int = {    
    def _max(xs: List[Int], maxNum: Int): Int = {   
      if (xs.isEmpty) maxNum
      else {
        val max = {
          if (maxNum >= xs.head) maxNum
          else xs.head
        }
        _max(xs.tail, max)
      }
    }
    _max(xs.tail, xs.head)
  }
6
Tay Wee Wen

Tôi chỉ sử dụng head () và tail ()

def max(xs: List[Int]): Int = {
    if (xs.isEmpty) throw new NoSuchElementException
    else maxRecursive(xs.tail,xs.head) 
  }

  def maxRecursive(xs: List[Int], largest: Int): Int = {
    if (!xs.isEmpty ){
      if (xs.head > largest) maxRecursive(xs.tail, xs.head)
      else maxRecursive(xs.tail, largest)
    }else{
      largest
    }
  }

Đây là bài kiểm tra:

test("max of a few numbers") {
    assert(max(List(3, 7, 2, 1, 10)) === 10)
    assert(max(List(3, -7, 2, -1, -10)) === 3)
    assert(max(List(-3, -7, -2, -5, -10)) === -2)
  }
1
Roman Kazanovskyi
def max(xs: List[Int]): Int = {
  def _max(xs: List[Int], maxAcc:Int): Int = {
    if ( xs.isEmpty ) 
      maxAcc 
    else 
      _max( xs.tail, Math.max( maxAcc, xs.head ) ) // tail call recursive
  }

  if ( xs.isEmpty ) 
    throw new NoSuchElementException() 
  else 
    _max( xs, Int.MinValue );
}
0
Andreas

list.sortWith (_>) .head & list.sortWith (> _). Reverse.head cho số lớn nhất và nhỏ nhất

0
tejas
  1. Gấp có thể giúp:

    if(xs.isEmpty)
      throw new NoSuchElementException
    else
      (Int.MinValue /: xs)((max, value) => math.max(max, value))
    
  2. Kết hợp danh sách và mẫu ( đã cập nhật , nhờ @ x3ro)

    def max(xs:List[Int], defaultValue: =>Int):Int = {
      @tailrec
      def max0(xs:List[Int], maxSoFar:Int):Int = xs match {
        case Nil => maxSoFar
        case head::tail => max0(tail, math.max(maxSoFar, head))
      }
      if(xs.isEmpty)
        defaultValue
      else
        max0(xs, Int.MinValue)
    }
    

(Giải pháp này không tạo ra cá thể Option mỗi lần. Ngoài ra, nó là đệ quy đuôi và sẽ nhanh như một giải pháp bắt buộc.)

0
Arseniy Zhizhelev

Đây là mã của tôi (tôi là người mới trong lập trình chức năng) và tôi cho rằng bất cứ ai gặp phải câu hỏi này sẽ là những người như tôi. Câu trả lời hàng đầu, trong khi tuyệt vời, là quá nhiều cho người mới tham gia! Vì vậy, đây là câu trả lời đơn giản của tôi. Lưu ý rằng tôi đã được yêu cầu (như một phần của Khóa học) để thực hiện việc này chỉ bằng đầu và đuôi.

/**
   * This method returns the largest element in a list of integers. If the
   * list `xs` is empty it throws a `Java.util.NoSuchElementException`.
   *
   * @param xs A list of natural numbers
   * @return The largest element in `xs`
   * @throws Java.util.NoSuchElementException if `xs` is an empty list
   */
    @throws(classOf[Java.util.NoSuchElementException])
    def max(xs: List[Int]): Int = find_max(xs.head, xs.tail)

    def find_max(max: Int, xs: List[Int]): Int = if (xs.isEmpty) max else if (max >= xs.head) find_max(max, xs.tail) else find_max(xs.head, xs.tail)

Một số xét nghiệm:

test("max of a few numbers") {
    assert(max(List(3, 7, 2)) === 7)
    intercept[NoSuchElementException] {
      max(List())
    }
    assert(max(List(31,2,3,-31,1,2,-1,0,24,1,21,22)) === 31)
    assert(max(List(2,31,3,-31,1,2,-1,0,24,1,21,22)) === 31)
    assert(max(List(2,3,-31,1,2,-1,0,24,1,21,22,31)) === 31)
    assert(max(List(Int.MaxValue,2,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,22)) === Int.MaxValue)
  }
0
Anand

Nếu bạn được yêu cầu viết hàm max đệ quy trên danh sách bằng cách sử dụng isEmpty, head và tail và ném ngoại lệ cho danh sách trống:

def max(xs: List[Int]): Int =
  if (xs.isEmpty) throw new NoSuchElementException("max of empty list")
  else if (xs.tail.isEmpty) xs.head
  else if (xs.head > xs.tail.head) max(xs.head :: xs.tail.tail)
  else max(xs.tail)

nếu bạn sử dụng hàm max trong danh sách thì đơn giản là (bạn không cần phải viết hàm đệ quy của riêng mình):

val maxInt = List(1, 2, 3, 4).max
0
Bilal Wahla

Có vẻ như bạn chỉ mới bắt đầu với scala nên tôi cố gắng đưa ra câu trả lời đơn giản nhất cho câu trả lời của bạn, làm thế nào để đệ quy: 

 def max(xs: List[Int]): Int = {
   def maxrec(currentMax : Int, l: List[Int]): Int = l match {
     case Nil => currentMax
     case head::tail => maxrec(head.max(currentMax), tail) //get max of head and curretn max
   }
   maxrec(xs.head, xs)
 }

Phương thức này xác định một phương thức bên trong riêng (maxrec) để xử lý đệ quy. Nó sẽ thất bại (ngoại trừ) bạn cung cấp cho nó một danh sách trống (không có tối đa trên Danh sách trống)

0
Chirlo

Với đệ quy đuôi

  @tailrec
  def findMax(x: List[Int]):Int =  x match {
    case a :: Nil => a
    case a :: b :: c =>  findMax( (if (a > b) a else b) ::c)
  }
0
BiN