2010-11-15 16 views
8

nói rằng, tôi có một loạt các chức năng "xác thực" trả về Không nếu không có lỗi, ngược lại nó trả về một số (Chuỗi) chỉ định thông báo lỗi. Một cái gì đó như sau ...Làm cách nào để đảo ngược luồng của Tùy chọn đơn lẻ?

def validate1:Option[String] 
def validate2:Option[String] 
def validate3:Option[String] 

Tôi sẽ gọi cho họ theo trình tự và ngay sau khi một trả về Một số (Chuỗi), tôi dừng lại và trả về tương tự. Nếu nó trả về Không, tôi chuyển sang bước tiếp theo cho đến khi chuỗi kết thúc. Nếu tất cả trong số họ trả về None, tôi trả về None.

Tôi muốn dán chúng lại với nhau trong "biểu thức". Một cái gì đó như ...

for(a <- validate1; b <- validate2; c <- validate3) yield None; 

Tuy nhiên, tùy chọn chảy chính xác ngược lại những gì tôi muốn ở đây. Nó dừng tại None và sau với Some (String).

Tôi có thể đạt được điều gì đó như thế?

Trả lời

2

Bạn không thể kết hợp các bộ lặp với nhau và sau đó lấy phần tử đầu tiên? Một cái gì đó như:

scala> def validate1: Option[String] = {println("1"); None} 
scala> def validate2: Option[String] = {println("2"); Some("error")} 
scala> def validate3: Option[String] = {println("3"); None} 
scala> (validate1.iterator ++ validate2.iterator ++ validate3.iterator).next 
1 
2 
res5: String = error 
+0

Dòng cuối cùng có thể được đơn giản hóa một chút: '(validate1 ++ validate2 ++ validate3) .head' – pr1001

+2

Tôi không nghĩ rằng bạn muốn thả các cuộc gọi '.iterator' - sau đó bạn sẽ luôn gọi tất cả các phương thức xác thực thay vì chỉ các phương thức gọi cho đến khi một trong số chúng trả về lỗi. – Steve

+1

Ahh, tôi thấy, rất thông minh. – pr1001

17

Bạn chỉ có thể chuỗi các cuộc gọi cùng với OrElse phương pháp trên Tùy chọn

validate1 orElse validate2 orElse validate3 

hoặc bạn có thể chạy một lần so với một tập hợp các phương pháp Validate chuyển đổi sang chức năng

val vlist= List(validate1 _, validate2 _, validate3 _) 

vlist.foldLeft(None: Option[String]) {(a, b) => if (a == None) b() else a} 
+3

+1 Tôi thậm chí sẽ kết hợp hai giải pháp này để có dạng ngắn hơn và dễ đọc hơn: l.foldLeft (Không: Tùy chọn [String]) {(a, b) => a orElse b()} – mcveat

+0

Tôi thích chuỗi orElse . Cảm ơn Don. – sanjib

+0

@mcveat, đó là một sự tinh tế tốt đẹp, @sanjib, đó là một câu hỏi hay, orElse dường như xuất hiện khá thường xuyên trên SO. –

0

Tôi nghĩ bạn có thể hưởng lợi từ việc sử dụng số Box của Lift, trong đó có Full (ví dụ: Some), Empty (tức là None) và Failure (một số Empty với lý do tại sao nó trống và có thể bị xích). David Pollak có một số good blog post giới thiệu nó. Nói tóm lại, bạn có thể làm điều gì đó như thế này (không kiểm tra):

def validate1: Box[String] 
def validate2: Box[String] 
def validate3: Box[String] 
val validation = for (
    validation1 <- validate1 ?~ "error message 1" 
    validation2 <- validate2 ?~ "error message 2" 
    validation3 <- validate3 ?~ "error message 3" 
) yield "overall success message" 

Đây không phải là bất kỳ ngắn hơn ví dụ ban đầu nhưng nó, theo ý kiến ​​của tôi, một chút logic hơn, với kết quả của một xác nhận thành công trong một số Full và xác thực không thành công trong Failure.

Tuy nhiên, chúng tôi có thể thu nhỏ hơn. Đầu tiên phải kể chức năng xác nhận của chúng tôi trở Box[String], họ có thể trở lại Failure là chính mình và chúng tôi không cần phải chuyển đổi Empty-Failure mình:

val validation = for (
    validation1 <- validate1 
    validation2 <- validate2 
    validation3 <- validate3 
) yield "overall success message" 

Nhưng, Box cũng có một phương pháp or trả về cùng Box nếu nó là Full hoặc khác Box nếu không. Điều này sẽ cung cấp cho chúng tôi:

val xác nhận = validate1 hoặc validate2 hoặc validate3

Tuy nhiên, dòng đó dừng lại ở người đầu tiên xác nhận thành công, không phải là thất bại đầu tiên. Nó có thể làm cho tinh thần để thực hiện một phương pháp mà làm những gì bạn muốn (có lẽ gọi là unless?) Mặc dù tôi không thể nói rằng nó thực sự sẽ hữu ích hơn nhiều so với cách tiếp cận hiểu.

Tuy nhiên, đây là một thư viện nhỏ yếu đuối nào đó:

scala> class Unless[T](a: Box[T]) { 
    | def unless(b: Box[T]) = { 
    | if (a.isEmpty) { a } 
    | else b 
    | } 
    | } 
defined class Unless 

scala> implicit def b2U[T](b: Box[T]): Unless[T] = new Unless(b) 
b2U: [T](b: net.liftweb.common.Box[T])Unless[T] 

scala> val a = Full("yes")          
a: net.liftweb.common.Full[java.lang.String] = Full(yes) 

scala> val b = Failure("no")          
b: net.liftweb.common.Failure = Failure(no,Empty,Empty) 

scala> val c = Full("yes2")          
c: net.liftweb.common.Full[java.lang.String] = Full(yes2) 

scala> a unless b 
res1: net.liftweb.common.Box[java.lang.String] = Failure(no,Empty,Empty) 

scala> a unless b unless c 
res2: net.liftweb.common.Box[java.lang.String] = Failure(no,Empty,Empty) 

scala> a unless c unless b 
res3: net.liftweb.common.Box[java.lang.String] = Failure(no,Empty,Empty) 

scala> a unless c 
res4: net.liftweb.common.Box[java.lang.String] = Full(yes2) 

Đây là một hack nhanh chóng dựa trên sự hiểu biết hạn chế của tôi về hệ thống kiểu Scala, như bạn có thể thấy trong các lỗi sau:

scala> b unless a 
<console>:13: error: type mismatch; 
found : net.liftweb.common.Full[java.lang.String] 
required: net.liftweb.common.Box[T] 
     b unless a 
       ^

Tuy nhiên, điều đó là đủ để giúp bạn đi đúng hướng.

Tất nhiên, Lift ScalaDocs có thêm thông tin về Box.

3

Thư viện scalaz có loại gọi là Validation cho phép một số môn thể dục dụng cụ đáng kinh ngạc với việc xây dựng cả lỗi và thành công. Ví dụ, giả sử bạn có một vài phương pháp mà có thể hoặc là trả về một thất bại tin nhắn hoặc một số kết quả thành công (A/B/C):

import scalaz._; import Scalaz._ 
def fooA : ValidationNEL[String, A] 
def fooB : ValidationNEL[String, B] 
def fooC : ValidationNEL[String, C] 

Đây có thể được sử dụng với các functor applicative đến chuỗi các cuộc gọi cùng :

(foo1 <|**|> (foo2, foo3)) match { 
    case Success((a, b, c)) => //woot 
    case Failure(msgs)  => //erk 
} 

Lưu ý rằng nếu bất kỳ một trong foo1/2/3 thất bại, sau đó toàn bộ thành phần không thành công với một danh sách không trống (NEL) các thông điệp thất bại. Nếu có nhiều lỗi, bạn sẽ nhận được tất cả các thông báo lỗi.

Đó là ứng dụng sát thủ. Ví dụ về cách tor trở lại là một thành công và thất bại như sau

def foo1 : ValidationNEL[String, Int] = 1.success 
def foo2 : ValidationNEL[String, Double] = "some error msg".failNel 
Các vấn đề liên quan