Trở lại Scala


83

Tôi là một lập trình viên scala mới và đã gặp phải một hành vi kỳ lạ.

def balanceMain(elem: List[Char]): Boolean =
  {
    if (elem.isEmpty)
      if (count == 0)
        true;
      else false;

    if (elem.head == '(')
      balanceMain(elem.tail, open, count + 1);....

Về cơ bản, tôi muốn trả về true nếu elem.isEmptycount == 0. Nếu không, tôi muốn trả về false.

Bây giờ ở trên tôi đã đọc rằng không cần thêm câu lệnh trả về trong scala. Vì vậy, tôi đã bỏ qua returnở trên. Nhưng nó không trả về boolean. Nếu tôi thêm một câu lệnh trả về là return true. nó hoạt động hoàn hảo. Tại sao nó như vậy?

Ngoài ra, tại sao nó được coi là một thực tiễn xấu khi có câu lệnh trả về trong scala


2
thường không có nhu cầu sử dụng từ khóa trở lại, miễn là bạn phá vỡ mã của bạn vào các phương pháp đủ nhỏ.
mauhiz 24/09/12

@mauhiz Cảm ơn. Bạn có thể vui lòng giải thích nó? Bạn sẽ làm như thế nào.
Jatin

4
có vẻ như bạn đang tham gia khóa học coursera scala. tất cả những điều tốt nhất :)
weima

Câu trả lời:


139

Nó không đơn giản như chỉ bỏ qua returntừ khóa. Trong Scala, nếu không có returnthì biểu thức cuối cùng được coi là giá trị trả về. Vì vậy, nếu biểu thức cuối cùng là những gì bạn muốn trả về, thì bạn có thể bỏ qua returntừ khóa. Nhưng nếu những gì bạn muốn trả lại không phải là biểu thức cuối cùng, thì Scala sẽ không biết rằng bạn muốn trả lại nó .

Một ví dụ:

def f() = {
  if (something)
    "A"
  else
    "B"
}

Ở đây, biểu thức cuối cùng của hàm flà một biểu thức if / else đánh giá thành một Chuỗi. Vì không có returndấu rõ ràng , Scala sẽ suy ra rằng bạn muốn trả về kết quả của biểu thức if / else này: a String.

Bây giờ, nếu chúng ta thêm một cái gì đó sau biểu thức if / else:

def f() = {
  if (something)
    "A"
  else
    "B"

  if (somethingElse)
    1
  else
    2
}

Bây giờ biểu thức cuối cùng là một biểu thức if / else đánh giá một Int. Vì vậy, kiểu trả về của fsẽ là Int. Nếu chúng tôi thực sự muốn nó trả về Chuỗi, thì chúng tôi sẽ gặp rắc rối vì Scala không biết rằng đó là những gì chúng tôi dự định. Do đó, chúng ta phải sửa nó bằng cách lưu trữ Chuỗi vào một biến và trả về sau biểu thức if / else thứ hai, hoặc bằng cách thay đổi thứ tự để phần Chuỗi xảy ra cuối cùng.

Cuối cùng, chúng tôi có thể tránh returntừ khóa ngay cả với biểu thức if-else lồng nhau như của bạn:

def f() = {
  if(somethingFirst) {
    if (something)      // Last expression of `if` returns a String
     "A"
    else
     "B"
  }
  else {
    if (somethingElse)
      1
    else
      2

    "C"                // Last expression of `else` returns a String
  }

}


4
Bởi tất cả các vị thần, cảm ơn! Tôi đã chiến đấu với cùng một vấn đề trong nhiều giờ (cũng là khi thực hiện khóa học ở Coursera) và không thể hiểu tại sao phải trả lại.
Igor Rodriguez

đối với ví dụ đầu tiên, điều gì sẽ xảy ra nếu bạn thêm trả về. tức là return "A"return "B"?
SamAko,

@ T.Rex nó sẽ trả về trình gọi của f () với giá trị "A" hoặc "B". Nhưng như tôi đã giải thích trong câu trả lời của mình. Không bao giờ sử dụng trả lại trong Scala. Nếu các câu lệnh trong Scala hoạt động theo cách chức năng. Họ đánh giá một cái gì đó với một loại. Giống như toán tử bậc ba trong Java (? :). Ví dụ: val foo = if (mybool) "A" else "B" - foo sẽ là một Chuỗi chứa "A" hoặc "B". Tương tự như vậy, hãy nghĩ về một hàm không trả về một cái gì đó mà là đánh giá giá trị của biểu thức cuối cùng trong nó.
Grmpfhmbl

23

Chủ đề này thực sự phức tạp hơn một chút như được mô tả trong các câu trả lời cho đến nay. Bài đăng trên blog này của Rob Norris giải thích nó chi tiết hơn và đưa ra các ví dụ về việc khi nào việc sử dụng return sẽ thực sự phá vỡ mã của bạn (hoặc ít nhất là có những tác động không rõ ràng).

Tại thời điểm này, hãy để tôi chỉ trích dẫn bản chất của bài đăng. Tuyên bố quan trọng nhất là ngay trong phần đầu. In cái này làm áp phích và dán lên tường của bạn :-)

Các returntừ khóa không phải là “bắt buộc” hay “suy ra”; nó thay đổi ý nghĩa của chương trình của bạn và bạn không bao giờ nên sử dụng nó.

Nó đưa ra một ví dụ, nơi nó thực sự phá vỡ một cái gì đó, khi bạn nội dòng một hàm

// Inline add and addR
def sum(ns: Int*): Int = ns.foldLeft(0)((n, m) => n + m) // inlined add

scala> sum(33, 42, 99)
res2: Int = 174 // alright

def sumR(ns: Int*): Int = ns.foldLeft(0)((n, m) => return n + m) // inlined addR

scala> sumR(33, 42, 99)
res3: Int = 33 // um.

bởi vì

Một returnbiểu thức, khi được đánh giá, sẽ loại bỏ tính toán hiện tại và quay trở lại trình gọi của phương thức returnxuất hiện trong đó .

Đây chỉ là một trong những ví dụ được đưa ra trong bài đăng được liên kết và nó là dễ hiểu nhất. Còn nhiều hơn nữa và tôi rất khuyến khích bạn, hãy đến đó, đọc và hiểu.

Khi bạn đến từ các ngôn ngữ bắt buộc như Java, điều này thoạt nghe có vẻ kỳ quặc, nhưng khi bạn đã quen với phong cách này thì nó sẽ có ý nghĩa. Hãy để tôi kết thúc bằng một trích dẫn khác:

Nếu bạn rơi vào tình huống mà bạn nghĩ rằng bạn muốn trở về sớm, bạn cần phải suy nghĩ lại cách bạn đã xác định tính toán của mình.


12
Tôi không đồng ý với Rob, IMO bạn không được returnchỉ sử dụng trong các biểu thức lambda, nhưng đây là một lựa chọn thiết kế tồi tệ trong ngôn ngữ, mã thậm chí không nên biên dịch (trong python, điều này được tránh bằng cách có lambdatừ khóa mà không có câu lệnh trả về). .. trong mỗi trường hợp khác tôi không thấy một vấn đề thực sự trong việc sử dụng returnnếu bạn muốn quay trở lại một giá trị (và có lối ra từ phương pháp thực hiện vì là những gì trở lại được sử dụng cho trong tất cả các ngôn ngữ!)
daveoncode

2
Bạn có thể tự do đưa ra ý kiến ​​của mình, nhưng nhiều người đồng ý rằng sử dụng return trong Scala ít nhất là một phong cách tồi. Tôi dám bạn tìm các ví dụ tài liệu Scala chính thức sử dụng trả về. Nó thậm chí không được giải thích trong tài liệu chính thức của AFAIK, chỉ trong tài liệu đặc tả (chương 6.20). Trích lời chính Martin Odersky (Lập trình trong Scala) "Phong cách được khuyến nghị cho các phương thức trên thực tế là tránh có nhiều câu lệnh trả về rõ ràng, và đặc biệt là nhiều câu lệnh trả về. Thay vào đó, hãy nghĩ về mỗi phương thức như một biểu thức mang lại một giá trị, được trả về." Ed đầu tiên của cuốn sách này hiện có sẵn trực tuyến miễn phí artima.com/pins1ed
Grmpfhmbl

4

Tôi không lập trình Scala, nhưng tôi sử dụng một ngôn ngữ khác có trả về ngầm định (Ruby). Bạn có mã sau if (elem.isEmpty)khối của mình - dòng mã cuối cùng là những gì được trả về, đó là lý do tại sao bạn không nhận được những gì bạn mong đợi.

CHỈNH SỬA: Đây cũng là một cách đơn giản hơn để viết hàm của bạn. Chỉ cần sử dụng giá trị boolean của isEmpty và đếm để trả về true hoặc false tự động:

def balanceMain(elem: List[Char]): Boolean =
{
    elem.isEmpty && count == 0
}

Cảm ơn. Nhưng tôi chỉ muốn trả về nếu elem.isEmpty && count == 0 trả về true, còn lại tiếp tục khối. Ở trên sẽ trả về ngay cả khi nó là sai.
Jatin

@Jatin: Ah, không nhận ra điều đó. Sớm và explicittrở lại sẽ là thích hợp trong trường hợp này.
jmdeldin

@Frank: Nó cũng không được định nghĩa trong mã của OP. Tôi cho rằng đó là một cuộc gọi phương thức.
jmdeldin

4

Đừng viết các ifcâu lệnh mà không có một tương ứng else. Khi bạn thêm elseđoạn mã vào, bạn sẽ thấy rằng của bạn truefalsetrên thực tế là biểu thức cuối cùng của hàm.

def balanceMain(elem: List[Char]): Boolean =
  {
    if (elem.isEmpty)
      if (count == 0)
        true
      else
        false
    else
      if (elem.head == '(')
        balanceMain(elem.tail, open, count + 1)
      else....

5
Tôi đã áp dụng nó, có 3 IF lồng nhau và nó trông xấu xí. Có bất kỳ mẫu nào để làm cho nó trông đẹp hơn không?
Capacytron

4

Theo mặc định, biểu thức cuối cùng của một hàm sẽ được trả về. Trong ví dụ của bạn có một biểu thức khác sau điểm, nơi bạn muốn giá trị trả về của mình. Nếu bạn muốn trả lại bất kỳ thứ gì trước biểu thức cuối cùng của mình, bạn vẫn phải sử dụng return.

Bạn có thể sửa đổi ví dụ của mình như thế này, để trả về một Booleantừ phần đầu tiên

def balanceMain(elem: List[Char]): Boolean = {
  if (elem.isEmpty) {
    // == is a Boolean resulting function as well, so your can write it this way
    count == 0
  } else {
    // keep the rest in this block, the last value will be returned as well
    if (elem.head == "(") {
      balanceMain(elem.tail, open, count + 1)
    }
    // some more statements
    ...
    // just don't forget your Boolean in the end
    someBoolExpression
  }
}

9
Không phải "tuyên bố". "biểu thức"
Viktor Klang

0

Trường hợp sử dụng phù hợp với mục đích trả lại sớm. Nó sẽ buộc bạn phải khai báo tất cả các nhánh trả về một cách rõ ràng, ngăn ngừa lỗi bất cẩn khi quên viết return ở đâu đó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.