我一直在学习F#,我正在编写一个工具。除非我需要处理无效输入或异常情况,否则一切正常。
在F#中处理此问题的推荐方法是什么?构造和使用我的函数的方式我不知道如何发回一条消息告诉调用函数,由于输入错误而导致调用失败,并且需要继续执行类似于C#的循环。
我因失去回归而受苦。我可以从F#函数返回其中的任何位置,还是必须在最后一行?
以下是我的一些代码示例,其中输入在某些情况下基本上是空的,我正在检测它。我需要阻止运行过程中的步骤。
let SchemaQueryString s =
let sqsValid s =
let Strcat (x : string) (y : string) = x + ", " + y;
let RecFieldName (x : MeasurementRecord) = x.FieldName;
let mapr = List.map RecFieldName s.MeasurementTypes;
let cores = List.reduce Strcat mapr;
"SELECT ProductionCode, TestTime, " + cores + " FROM " + s.TemplateName + " ORDER BY ProductionCode, TestTime";
match s.MeasurementTypes.Length with
| 0 -> ""
| _ -> sqsValid s;
let TemplateMigration tcs rr =
let sql = SchemaQueryString tcs;
let sqlc = new SqlCommand(sql, QCOld);
let sqldr = sqlc.ExecuteReader();
ignore (sqldr.Read());
...
我需要中止TemplateMigration函数并立即返回sql变量与“”的匹配。或构造某种类型的联合类型,表明不能构造sql查询字符串,因为对于特定情况,它无法完成。
感谢您的帮助。我正在逐渐搞清楚这种语言,我非常喜欢它,但我正在努力解决它的某些方面,我不知道如何取代关于如何用功能性思想做某事的程序性想法。
答案 0 :(得分:8)
第一次使用函数式语言时,很多人都认为一切都是表达式。这意味着使用控制流表达式执行控制流程。
那么语句和表达式之间的区别是什么?
语句没有价值,即它不是类型的实例。表达式具有值(即使该值属于特殊类型unit
,其具有单个实例 ()
,表示没有值。)
在过程语言中,我们使用语句来控制执行流程:
class Program
{
static void Main(string[] args)
{
string str;
if (DateTime.Now.DayOfWeek == DayOfWeek.Monday)
return;
else if (args.Length >= 2)
str = args[1];
else
str = "Hello";
Console.WriteLine(str);
}
}
我们在此定义str
类型的变量string
,其值为null
。然后我们要么忽略它,要么return
如果它是星期一,请分配args[1]
(如果可用)或"Hello"
否则打印出来。
当控制流语句长达几行时,这种编程是相当安全的。但是,只要语句变得大于页面长度,或者复杂和嵌套,我们就更有可能引入错误,通常是billion dollar mistake。
纯函数式语言使用控制流表达式。这意味着if
表达式具有值!让我们在前面的代码示例中慢慢实现控件。首先,我们可以解决分配字符串str
。
let str =
if args.Length >= 2 then
args.[1]
else
"Hello"
在这里,我们看到我们将if
表达式的值直接绑定到str
。 if
表达式的两个分支(子表达式)必须计算为string
类型的实例。我们简直不能忘记为string赋值,因为编译器会在我们尝试时抛出错误。
但是,在我们希望在控制流程的不同分支中执行不同类型的事情时,似乎存在许多情况。在我们的例子中,如果它是星期一,我们不想做任何事情。这就是歧视联盟的力量发挥作用的地方。这些允许我们将异构类型组合成一个整体,保留基于表达式的语言的强大功能,以便在每个级别对我们的代码进行类型检查。
在我们的例子中,我们不需要创建新的联合类型,因为option
类型已经包含此功能。因此我们重写:
open System
[<EntryPoint>]
let Main args =
let str =
if DateTime.Now.DayOfWeek = DayOfWeek.Monday then
None
elif args.Length >= 2 then
Some(args.[1])
else
Some("Hello")
match str with
| Some(str) -> Console.WriteLine(str)
| None -> ()
Environment.ExitCode
现在,我们从if
表达式返回的值为string option
(或C#中的Option<string>
),然后我们使用模式匹配来安全地处理所有我们控制流程的可能结果。
当您从unit
返回TemplateMigration
值时,您可以简单地从if
表达式的两个分支返回相同的值,以便:
let TemplateMigration tcs rr =
let sql = SchemaQueryString tcs
if sql = null || sql.Length = 0 then
()
else
let sqlc = new SqlCommand(sql, QCOld)
let sqldr = sqlc.ExecuteReader()
ignore (sqldr.Read())
但是,从option
SchemaQueryString
值可能会更好
let SchemaQueryString s =
... // Elided
match s.MeasurementTypes.Length with
| 0 -> None
| _ -> Some(sqsValid s)
在整个代码中“传播”强类型值。
答案 1 :(得分:4)
其他人已经发布了两个最早的返回方法:if-then-else(或等效的模式匹配)和异常。还有第三个,即创建异常monad并使用F#计算表达式语法来获得“隐式失败返回”代码路径。这是一种更先进的技术,除非你在许多函数中编写很多if-then-else子句,否则我会建议不要使用它,此时计算表达式可能会开始变为“胜利”。
在这个特定的例子中,ScemaQueryString返回“字符串选项”(例如,字符串选项)是最惯用的。得到回报
match s.MeasurementTypes.Length with
| 0 -> None
| _ -> Some(sqsValid s)
然后用
开始其他功能match SchemaQueryString tcs with
| None -> ()
| Some(sql) -> ... // rest of function
答案 2 :(得分:1)
您可以使用“throw”语句退出代码,但F#异常处理比OCaml重要得多(它不是F#quirk,它是.net功能)。
答案 3 :(得分:0)
与其他语言一样,您可以随时返回。插入条件,为不良数据返回特殊值(-1,“错误”等),继续处理并返回正确的数据。
或者使用F#的exception handling methods。