我想简化表达式if(x == 1 || x == 2)
我希望我能写if(x == 1 or 2)
,但没有任何语法。
其他可能性是使用Contains或Any方法,例如:if([1,2].Contains(x))
但这涉及不必要的调用。
我可以创建一些允许我这样做的运算符吗?
在Nemerle语言中我可以写宏:
macro @|||(left, right)
match (left)
| <[ $x == $y ]> => <[ $x == $y || $x == $right ]>
| _ => Message.Error("Error"); <[ ]>
然后用法:
if (x == 1 ||| 2) { .. }
我可以在F#中以这种方式创建运算符吗?
答案 0 :(得分:9)
我同意Brian的意见,即为了保存三个字符而构建一个宏可能不是一个好主意。这只会使程序更难阅读(对于那些不了解自定义宏或改变运算符含义的人来说)。
此外,您很可能使用标准F#构造(如模式匹配)以更简洁的方式编写相同的逻辑。例如:
match x with
| 1 | 2 -> printfn "yes"
| _ -> printfn "no"
惯用解决方案将取决于具体情况,这很难从你给出的例子来判断。
答案 1 :(得分:3)
您可以使用|>
来完成此操作,借用其中一个haskell monoid实例。
let appendResults f g = (fun x -> f(x) || g(x))
let f x = x=1
let g x = x=2
let inline (>>||) x y = (appendResults f g) x y
let x = 1
if(x |> (1 >>|| 2)) then printfn "true" else printfn "false"
对于任意数量的参数,只需模仿haskell中相关的mconcat
方法获得相同的效果,也许是这样的:
let rec concatResults = function
| [] -> (fun x -> false)
| (x:xs) -> appendResults x (concatResults xs)
老实说,你也可以使用Contains。如果有任何特殊的开销,我怀疑它真的很重要。
答案 2 :(得分:2)
我同意Brian和Tomas;它有点实用感觉来发明你自己的宏,可能只使用了几次 但是,从研究函数式语言的内部结构来看,我做非常有趣 考虑一下:
// Generic
let inline mOp1<'a> op sample x = op sample x, sample
let inline mOp2<'a> op1 op2 (b, sample) x = op1 b (op2 sample x), sample
// Implementation for (=) and (||)
let (==) = mOp1 (=)
let (|=) = mOp2 (||) (=)
// Use
let ret1 = x == 1 |= 2 |> fst
您可以在此处找到更多详细信息,其他运营商和效果衡量标准:https://stackoverflow.com/a/11552429/974789
答案 3 :(得分:0)
这有点hackish,但确实有效
let x = 1
let inline (|||) a b = [a;b]
let inline (==) a b = b |> List.exists (fun t -> t=a)
if x == (1 ||| 2) then printfn "true" else printfn "false"
它需要两个或等于的自定义运算符。修改它以支持任意或链
并不难当然如果你只需要2个数字就可以
let x = 1
let inline (|||) a b = (a,b)
let inline (==) a (c,d) = a=c ||a=d
if x == (1 ||| 2) then printfn "true" else printfn "false"
答案 4 :(得分:0)
这可以通过将元组转换为数组来实现,因此不要期望最佳性能。
let inline (==) a b =
Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields(b)
|> Array.exists((=) a)
示例:
3 == (1,2) // false
3 == (1,2,3) // true