Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Оператор перегрузки + в F #

Автор вопроса: Li HaoyiОтветов: 5

Итак, у меня есть это:

open System
open System.Linq
open Microsoft.FSharp.Collections
type Microsoft.FSharp.Collections.List<'a> with
    static member (+) (First : List<'a>) (Second : List<'a>) =
        First.Concat(Second)

let a = [1; 2; 3; 4; 54; 9]
let b = [3; 5; 6; 4; 54]


for x in List.(+) a b do
    Console.WriteLine(x)

и я хочу преобразовать последнюю строку в

for x in a + b do
    Console.WriteLine(x)

но это дает мне

The type 'int list' does not support any operands named '+'

Документация и примеры в сети отрывочны, и, несмотря на мой гугл-фу, я не смог заставить его работать. По сути, исходя из фона Python, я хочу, чтобы мой синтаксис управления списком был столь же кратким, как я привык: для него не должно быть более 1 символа в инфиксной нотации.

operator-overloading

Ответы

Ответ 1 · рейтинг 7 · pad

Во-первых, замещающие операторы следует объявлять в форме кортежа, а не в переносимой форме. В твоем случае:

type Microsoft.FSharp.Collections.List<'a> with
    static member (+) (first: List<'a>, second: List<'a>) =
        first.Concat(second)

Во-вторых, после того, как вы это исправите, компилятор выдает предупреждение "Extension members cannot provide operator overloads. Consider defining the operator as part of the type definition instead.". Есть некоторые обходные пути, которые подробно обсуждались в Оператор перегрузки в F #: (/).

Ответ 2 · рейтинг 9 · Brian

Обратите внимание, что @ уже является инфиксным оператором с 1 символом для объединения списков.

Ответ 3 · рейтинг 8 · Gus

На самом деле есть способ "переназначить" существующие операторы, используя статические ограничения и перегрузки.

type ListExtension = ListExtension with
    static member        (?<-) (ListExtension, a , b) = a @ b
    static member inline (?<-) (ListExtension, a , b) = a + b

let inline (+) a b = (?<-) ListExtension a b

// test

let lst = [1;2] + [3;4]
// val lst : int list = [1; 2; 3; 4]

let sum = 1 + 2 + 3 + 4
// val sum : int = 10

При использовании тернарного оператора статические ограничения будут автоматически выведены, другой вариант - создать метод и написать ограничения вручную. Первая перегрузка охватывает случай, который вы хотите добавить (списки), вторая - существующее определение.

Итак, теперь в вашем коде вы можете:

for x in (+) a b do
    Console.WriteLine(x)

И это не нарушит существующие (+) для числовых типов.

Ответ 4 · рейтинг 3 · Tomas Petricek

Как указано в других ответах, вы не можете добавить реализацию + к существующему типу, потому что члены расширения игнорируются, а автономная привязка let скрывает реализацию по умолчанию (перегруженную).

Если вы хотите использовать + (что на самом деле не нужно, потому что библиотека F # содержит оператор @), вам придется написать оболочку для списка F #, которая поддерживает оператор напрямую:

open System.Collections
open System.Collections.Generic

/// Wrapper for F# list that exposes '+' operator and
/// implements 'IEnumerable<_>' in order to work with 'for'
type PlusList<'T>(list : list<'T>) =
  member x.List = list
  static member (+) (first : PlusList<'a>, second : PlusList<'a>) =
    first.List @ second.List
  interface IEnumerable with
    member x.GetEnumerator() = (list :> IEnumerable).GetEnumerator()
  interface IEnumerable<'T> with
    member x.GetEnumerator() = (list :> IEnumerable<_>).GetEnumerator()

// Simple function to wrap list
let pl l = PlusList<_>(l)

let a = pl [1; 2; 3; 4; 54; 9]
let b = pl [3; 5; 6; 4; 54]

for x in a + b do
  System.Console.WriteLine(x)

Ответ 5 · Ankur

Я думаю, что перегрузка оператора с использованием метода расширения не работает. Что вы можете сделать, так это определить перегрузку глобального оператора для списка (+), используя:

let inline (+) (f : List<'a>) (s : List<'a>) = f.Concat(s)

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы