f#元编程:如果X = 1或2语法,是否可能?

时间:2022-09-11 10:31:58

I want to simplify expression if(x == 1 || x == 2).
I wish I could write if(x == 1 or 2) but there is no syntax for that.
Other possibility is to use Contains or Any method like: if([1,2].Contains(x)) but this involves unnecessary call.

我想要简化表达式(x == 1 || x == 2),我希望我可以写if(x == 1或2),但没有语法。另一种可能性是使用包含或任何方法,如:if([1,2].Contains(x)),但这涉及不必要的调用。

Can I create some operator which allows me to do this ?

我可以创建一个操作符让我这样做吗?

In Nemerle language I can write macro:

在Nemerle语言中,我可以写宏:

macro @|||(left, right)
  match (left)
    | <[ $x == $y ]> => <[ $x == $y || $x == $right ]>
    | _ => Message.Error("Error"); <[ ]>

And then usage:

然后使用方法:

if (x == 1 ||| 2) { .. }

Can I create operator in such way in F# ?

我能在f#中以这种方式创建操作符吗?

5 个解决方案

#1


3  

You could use |> to accomplish this, borrowing from a common use of one of the haskell monoid instances.

您可以使用|>来完成这个任务,从一个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"

For arbitrary numbers of arguments, just mimic the relevant mconcat method from haskell for the same effect, perhaps like this:

对于任意数量的参数,只需模仿haskell的相关mconcat方法的相同效果,可能如下所示:

let rec concatResults = function
| [] -> (fun x -> false)
| (x:xs) -> appendResults x (concatResults xs)

Honestly though, you may as well just use Contains. If there is any special overhead doing that I doubt it really matters.

老实说,你也可以只使用包含。如果有什么特别的开销,我怀疑这真的很重要。

#2


9  

I agree with Brian's comment that constructing a macro in order to save three characters is probably not a good idea. This will only make the program harder to read (for those who do not know your custom macros or changed meaning of operators).

我同意Brian的评论,构建一个宏来保存三个字符可能不是一个好主意。这只会使程序更难以阅读(对于那些不了解您的自定义宏或更改操作符含义的人)。

Moreover, it is quite likely that you could write the same logic in a more concise way using standard F# constructs like pattern matching. For example:

而且,很有可能您可以使用标准的f#结构(如模式匹配)以更简洁的方式编写相同的逻辑。例如:

match x with
| 1 | 2 -> printfn "yes"
| _     -> printfn "no"

The idiomatic solution will depend on the concrete case, which is hard to judge from the example you gave.

惯用的解决方案将取决于具体的案例,这很难从你给出的例子中判断出来。

#3


2  

I agree with Brian and Tomas; it makes a little practical sense to invent your own macros that might be used just a few times.
However, I do find it very interesting from the point of studying the internals of functional languages.
Consider this:

我同意布莱恩和托马斯的观点;发明可以使用几次的宏是有点实际意义的。然而,从研究函数式语言的内部结构的角度来看,我发现它非常有趣。考虑一下:

// 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

You may find more details, other operators, and performance measurement here: https://*.com/a/11552429/974789

您可以在这里找到更多的细节、其他操作符和性能度量:https://*.com/a/11552429/974789。

#4


0  

This is slightly hackish, but it does work

这有点乱,但确实有效。

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"

It requires a custom operator for both or and equals. It would not be hard to modify this to support arbitrary or chains

它需要一个自定义操作符,既可以也可以等于。要修改它以支持任意或链,并不难。

Of course if you only need 2 numbers you can do

当然,如果你只需要两个数字你就能做到。

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"

#5


0  

This works by converting a tupple to an array, so do not expect the best performance.

这是通过将一个tupple转换成一个数组来实现的,所以不要期望最好的性能。

let inline (==) a b = 
    Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields(b) 
        |> Array.exists((=) a)

Example:

例子:

3 == (1,2)      // false
3 == (1,2,3)    // true

#1


3  

You could use |> to accomplish this, borrowing from a common use of one of the haskell monoid instances.

您可以使用|>来完成这个任务,从一个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"

For arbitrary numbers of arguments, just mimic the relevant mconcat method from haskell for the same effect, perhaps like this:

对于任意数量的参数,只需模仿haskell的相关mconcat方法的相同效果,可能如下所示:

let rec concatResults = function
| [] -> (fun x -> false)
| (x:xs) -> appendResults x (concatResults xs)

Honestly though, you may as well just use Contains. If there is any special overhead doing that I doubt it really matters.

老实说,你也可以只使用包含。如果有什么特别的开销,我怀疑这真的很重要。

#2


9  

I agree with Brian's comment that constructing a macro in order to save three characters is probably not a good idea. This will only make the program harder to read (for those who do not know your custom macros or changed meaning of operators).

我同意Brian的评论,构建一个宏来保存三个字符可能不是一个好主意。这只会使程序更难以阅读(对于那些不了解您的自定义宏或更改操作符含义的人)。

Moreover, it is quite likely that you could write the same logic in a more concise way using standard F# constructs like pattern matching. For example:

而且,很有可能您可以使用标准的f#结构(如模式匹配)以更简洁的方式编写相同的逻辑。例如:

match x with
| 1 | 2 -> printfn "yes"
| _     -> printfn "no"

The idiomatic solution will depend on the concrete case, which is hard to judge from the example you gave.

惯用的解决方案将取决于具体的案例,这很难从你给出的例子中判断出来。

#3


2  

I agree with Brian and Tomas; it makes a little practical sense to invent your own macros that might be used just a few times.
However, I do find it very interesting from the point of studying the internals of functional languages.
Consider this:

我同意布莱恩和托马斯的观点;发明可以使用几次的宏是有点实际意义的。然而,从研究函数式语言的内部结构的角度来看,我发现它非常有趣。考虑一下:

// 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

You may find more details, other operators, and performance measurement here: https://*.com/a/11552429/974789

您可以在这里找到更多的细节、其他操作符和性能度量:https://*.com/a/11552429/974789。

#4


0  

This is slightly hackish, but it does work

这有点乱,但确实有效。

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"

It requires a custom operator for both or and equals. It would not be hard to modify this to support arbitrary or chains

它需要一个自定义操作符,既可以也可以等于。要修改它以支持任意或链,并不难。

Of course if you only need 2 numbers you can do

当然,如果你只需要两个数字你就能做到。

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"

#5


0  

This works by converting a tupple to an array, so do not expect the best performance.

这是通过将一个tupple转换成一个数组来实现的,所以不要期望最好的性能。

let inline (==) a b = 
    Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields(b) 
        |> Array.exists((=) a)

Example:

例子:

3 == (1,2)      // false
3 == (1,2,3)    // true