2012-03-02 42 views
27

EDIT: Tôi tiếp tục nhận được upvotes ở đây. Chỉ để ghi lại, tôi không còn nghĩ rằng điều này là quan trọng. Tôi không cần nó kể từ khi tôi đăng nó.Scala - mutable (var) tham số tham số phương thức

tôi muốn làm sau trong Scala ...

def save(srcPath: String, destPath: String) { 
    if (!destPath.endsWith('/')) 
     destPath += '/' 
    // do something 
} 

... nhưng tôi không thể beacuse destPath là một val. Có cách nào để khai báo destPath dưới dạng var?

Lưu ý: có những câu hỏi tương tự nhưng trong tất cả chúng, OP chỉ muốn sửa đổi mảng.

Xin đừng thông báo sau:

đột biến các thông số đầu vào thường được xem phong cách là xấu và làm cho nó khó khăn hơn để lý do về mã.

Tôi nghĩ rằng nó hợp lệ trong lập trình bắt buộc (Scala cho phép cả hai, phải không?) Và thêm một cái gì đó như tmpDestPath sẽ chỉ thêm lộn xộn.

CHỈNH SỬA: Đừng hiểu lầm. Tôi biết rằng các chuỗi không thể thay đổi và tôi không muốn tham chiếu đến tham chiếu vì tôi không muốn sửa đổi dữ liệu của người gọi. Tôi chỉ muốn thay đổi tham chiếu cục bộ thành chuỗi mà người gọi đã cho tôi chuỗi của tôi (ví dụ: orig + '/'). Tôi muốn sửa đổi giá trị đó chỉ trong phạm vi của phương pháp hiện tại. Hãy xem, điều này hoàn toàn hợp lệ trong Java:

void printPlusOne(int i) { 
    i++; 
    System.out.println("i is: " + i); 
    System.out.println("and now it's same: " + i); 
} 

Tôi không phải tạo biến mới và tôi không phải tính i + 1 hai lần.

+5

Sau khi làm rõ câu trả lời là: Bạn không có thể. – Debilski

+2

Đó là những gì tôi nghi ngờ. Tôi sẽ đăng nó lên scala-tranh luận. – woky

+0

Vâng, cộng đồng Scala không thực sự có lợi cho việc có thể trực tiếp sửa đổi các tham số chức năng, dù bằng giá trị hay tham chiếu. Lý do cũng giống như lý do tại sao Scala cũng thiếu một cái gì đó khác từ ví dụ của bạn: toán tử '++' đơn nhất cho các kiểu số. Những thứ như vậy có vẻ là một phong cách lập trình không hiệu quả, có chức năng phụ, đó là điều mà Scala thường khuyến khích bạn tránh. Vì nó là viết tắt, nếu bạn muốn liên tục thay đổi một tham số hàm, trước tiên bạn phải lưu nó vào một 'var', điều này làm cho ý định của bạn rõ ràng hơn, dù sao đi nữa! – Destin

Trả lời

9

JVM không cho phép truyền tham chiếu qua con trỏ tới đối tượng (đó là cách bạn làm điều này trong C++), vì vậy bạn không thể thực hiện chính xác những gì bạn muốn.

Một lựa chọn là để trả lại giá trị mới:

def save(srcPath: String, destPath: String): String = { 
    val newPath = (if (!destPath.endsWith("/")) destPath+'/' else destPath) 
    // do something 
    newPath 
} 

khác là tạo một wrapper:.

case class Mut[A](var value: A) {} 

def save(srcPath: String, destPath: Mut[String]) { 
    if (!destPath.value.endsWith("/")) destPath.value += '/' 
    // do something 
} 

mà người dùng sau đó sẽ phải sử dụng trên đường trong (Tất nhiên, họ sẽ bị cám dỗ để save("/here",Mut("/there")) mà sẽ vứt bỏ những thay đổi, nhưng điều này luôn luôn là trường hợp với đối số chức năng vượt qua tham chiếu.)


Chỉnh sửa: những gì bạn đang đề xuất là một trong những nguồn gây nhầm lẫn lớn nhất trong số các lập trình viên không chuyên gia. Tức là, khi bạn sửa đổi đối số của một hàm, bạn có sửa đổi một bản sao cục bộ (truyền theo giá trị) hay bản sao gốc (chuyển qua tham chiếu) không? Nếu bạn thậm chí không thể sửa đổi nó nó là khá rõ ràng rằng bất cứ điều gì bạn làm là một bản sao địa phương.

Chỉ cần thực hiện theo cách đó.

val destWithSlash = destPath + (if (!destPath.endsWith("/")) "/" else "") 

Điều đáng nói là thiếu sự nhầm lẫn về những gì đang thực sự xảy ra.

+1

Xin chào. Cảm ơn bạn. Tôi sợ bạn hiểu lầm câu hỏi của tôi. Tôi đã cập nhật nó. – woky

+1

"JVM không cho phép truyền tham chiếu qua con trỏ tới đối tượng" Điểm này yêu cầu một số làm rõ rất cẩn thận. Khi truyền một nguyên thủy trong Java: chúng ta thực sự đi qua giá trị. Nhưng, khi truyền một đối tượng: bạn đang ở trong thực tế, chuyển một tham chiếu đến đối tượng đó (mà chúng ta có thể yêu cầu cũng đang được thực hiện: theo giá trị). Vì lý do này, câu lệnh "Java chỉ truyền theo giá trị" thực sự gây ra nhiều nhầm lẫn nhưng thực sự xác nhận đó đòi hỏi sự hiểu biết rằng các nguyên thủy được truyền theo giá trị và tham chiếu * đối tượng * được truyền theo giá trị. –

1

Đối tượng chuỗi không thay đổi trong Scala (và Java).Các lựa chọn thay thế tôi có thể nghĩ đến là:

  1. Trả về chuỗi kết quả dưới dạng giá trị trả về.
  2. Thay vì sử dụng tham số String, hãy sử dụng StringBuffer hoặc StringBuilder, không phải là bất biến.

Trong kịch bản thứ hai bạn sẽ có một cái gì đó như:

def save(srcPath: String, destPath: StringBuilder) { 
    if (!destPath.toString().endsWith("/")) 
     destPath.append("/") 
    // do something 
    // 
} 

EDIT

Nếu tôi hiểu đúng, bạn muốn sử dụng lập luận như là một biến địa phương. Bạn không thể, bởi vì tất cả các đối số phương pháp là val trong Scala. Điều duy nhất cần làm là sao chép nó vào biến cục bộ trước:

def save(srcPath: String, destPath: String) { 
    var destP = destPath 
    if (!destP.endsWith("/")) 
     destP += "/" 
    // do something 
    // 
} 
+1

Xin chào. Cảm ơn bạn. Tôi sợ bạn hiểu lầm câu hỏi của tôi. Tôi đã cập nhật nó. – woky

+0

Quảng cáo 2. Không phải là một tùy chọn, đó chỉ là sự lộn xộn. StringBuilder ngụ ý tôi sẽ xây dựng một số chuỗi lớn. Tại sao làm "String -> StringBuilder -> String" nếu tôi có thể làm "String (-> có thể mới String)"? Giải pháp của bạn chỉ là giải pháp cho việc thiếu kiến ​​thức về Scala hoặc giới hạn của Scala. Bạn sẽ không làm điều này trong Java, phải không? – woky

+0

Tôi không nói rằng việc sử dụng StringBuilder là thanh lịch, nhưng nếu bạn không muốn trả về một chuỗi như một giá trị trả về thì bạn phải sử dụng một đối số của một số loại chuỗi có thể thay đổi. Trên thực tế bạn có thể sử dụng endsWith() trực tiếp trên một StringBuilder trong Scala (bạn không cần phải gọi toString()). – Giorgio

24

Bạn không thể.

Bạn sẽ phải khai báo thêm var (hoặc sử dụng phong cách chức năng hơn :-)).

ví dụ đơn giản:

def save(srcPath: String, destPath: String) { 
    val normalizedDestPath = 
     if (destPath.endsWith('/')) destPath 
     else destPath + '/' 
    // do something with normalizedDestPath 
} 
+0

Tôi đã thêm một ví dụ. –

0

Dưới đây là một vài gợi ý:

1) Cập nhật chức năng của bạn một chút

def save(srcPath: String, destPath: String) { 
    var dp = destPath 
    if (!dp.endsWith('/')) 
    dp+= '/' 
    // do something, but with dp instead of destPath 
} 

2) Tạo một chức năng tiện ích để sử dụng trước khi gọi tiết kiệm

def savedPath(path: String) = 
    if(path.endsWith("/")) 
    path 
    else 
    path + "/" 

//call your save method on some path 
val myDestPath = ... 
val srcPath = ... 
save(srcPath, savedPath(myDestPath)) 
0

Không, điều đó không được phép ở Scala. Những người khác đã mô tả một số cách giải quyết cấp thấp (tất cả đều tốt), nhưng tôi sẽ thêm một mức cao hơn. Đối với loại mục đích bình thường hóa chuỗi này, tôi giữ xung quanh một phần mở rộng pimped để scala.String với các phương thức như hậu tố, tiền tố, removeSuffix và removePrefix. hậu tố và tiền tố nối thêm hoặc thêm một chuỗi vào một chuỗi khác, trừ khi hậu tố hoặc tiền tố đã có. removeSuffix và removePrefix làm rõ ràng, loại bỏ một chuỗi từ đầu hoặc cuối của chuỗi khác, nếu nó hiện diện. Trường hợp sử dụng của bạn sẽ được viết

val normalizedPath = destPath.addSuffix("/") 

Nếu bạn thực hiện một loạt các hoạt động phân tích dữ liệu hoặc tệp, các phương pháp này sẽ không thể tin rằng bạn đã từng không có chúng.

+0

Bạn đã kiểm tra 'stripPrefix' và' stripSuffix' trong 'StringLike' chưa? Có vẻ như họ đã làm những gì 'removePrefix' và' removeSuffix' của bạn làm. –

3

Có lẽ bạn có thể nhận được các hệ thống kiểu để làm công việc cho bạn, vì vậy bạn thậm chí không cần phải lo lắng về việc thêm một dấu gạch chéo mỗi lần:

class SlashString(s: String) { 
    override val toString = if (s endsWith "/") s else s + "/" 
} 
implicit def toSlashString(s: String) = new SlashString(s) 

Bây giờ bạn không cần bất kỳ mã nào tất cả để thay đổi đầu vào String:

def save(srcPath: String, destPath: SlashString) { 
    printf("saving from %s to %s", srcPath, destPath) 
} 

val src: String = "abc" 
val dst: String = "xyz" 

scala> save(src, dst) 
saving from abc to xyz/ 

Đúng vậy, có một chút về thiết lập lúc bắt đầu, nhưng điều này sẽ ít-so với các lớp học tiềm ẩn trong phiên bản 2.10, và nó loại bỏ tất cả sự lộn xộn từ phương pháp, đó là những gì bạn đang lo lắng.

0

Tôi biết đây là một câu hỏi cũ, nhưng nếu bạn chỉ muốn sử dụng lại tên tham số lẽ:

def save(srcPath: String, destPath: String) { 
    ((destPath: String) => { 
    // do something 
    })(if (!destPath.endsWith('/')) destPath + '/' else destPath) 
} 
Các vấn đề liên quan