Проблема бесконечного цикла со статическими операторами в расширениях Swift (цели обучения)

У меня возникла проблема с использованием статических функций + и * в расширении Swift. Я просто хочу, чтобы + был * и * был +. Когда я использую только оператор +, он работает нормально, но когда я включаю оба оператора, я сталкиваюсь с бесконечным циклом.

Работает отлично

extension Int {
    static func + (left: Int, right: Int) -> Int {
        return (left * right)
    }
}

// 12
print(3 + 4)
// 12
print(3 * 4)

Теперь переходим в бесконечный цикл

extension Int {
    static func + (left: Int, right: Int) -> Int {
        return (left * right)
    }
    
    static func * (left: Int, right: Int) -> Int {
        return (left + right)
    }
}

print(3 + 4)
print(3 * 4)

Мы ценим любые предложения.


Классный трюк для отладки: используйте print(#function), см. ниже в комментариях.

Как-то мне нужно сохранить локальный +? Похоже на: «Поменять местами два числа»?

Adrian 19.07.2024 15:44

Вопрос: Когда вы делаете return (left + right)(left + right) какая операция должна быть? «Настоящий +» или переопределенный «+»? Это ваша проблема.

Larme 19.07.2024 16:02

Измените умножение на Int(Double(left) + Double(right)), и это «исправит», понимаете, почему?

Joakim Danielson 19.07.2024 16:33

@Larme Я смотрел на все это примерно так: внутри тела + и * каким-то образом не меняются (не знаю, как создается +/*). Когда я говорю «статическая функция +», я надеялся, что, возможно, я сделаю какое-то затенение. Итак, + и *, поставляемые с языком, возможно, не изменились, и мое расширение каким-то образом идет поверх этого, я не знаю...

Adrian 19.07.2024 16:35

@JoakimDanielson, это означает, что есть + для Int и один для double? Или что-то вроде того, как метод разрешен внутри класса с тем же именем и разными типами? Значит, у каждого типа может быть несколько плюсов? (Не для каждого типа, но, возможно, для каждой версии отдельного аргумента). Но я полагаю, что + — это инфикс, верно?

Adrian 19.07.2024 17:46

Похоже, print(3.0 + 4.0) возвращает 7.0. Я хотел сделать это только для Int, а не пропустить Int * и использовать Double, а затем преобразовать его обратно в Int. Возможно, позже я расширию функцию или что-то еще, что выполняет преобразование, и эта штука больше не будет работать.

Adrian 19.07.2024 17:53

Мое предложение было для int, так что все равно сделайте 3 + 4. Просто чтобы проверить, понимаете ли вы, почему у вас бесконечный цикл, верно?

Joakim Danielson 19.07.2024 18:13

@JoakimDanielson Понятно. Итак, это означает, что + — это функция внутри класса Int, * также является функцией внутри класса Int, теперь я думаю, что происходит: 1. Какая-то взаимная рекурсия? Я думаю, что Swift позволяет это, я определенно использовал это раньше. 2. Все ли в логике и как она переопределяется?

Adrian 19.07.2024 18:25

Не знаю почему, но я думаю, что, возможно, здесь есть что-то еще, какие-то глубокие вещи в коде. Может быть, * реализуется с помощью +?

Adrian 19.07.2024 18:30

Добавьте print(#function) в первую строку каждой функции, и вы увидите, что они вызывают друг друга.

Joakim Danielson 19.07.2024 18:37

Молодец, классная отладка. Похоже, у него нет базового варианта для завершения рекурсии. Опять же, я думаю, что это просто какие-то глубокие вещи, потому что, если я скажу 5 + 3 или 5 * 3, я не достигну базового случая, я предполагаю, что компилятор (или Xcode или что-то еще) делает это с помощью некоторых алгоритмов.

Adrian 19.07.2024 18:41

ааааа, базовый случай умножения отличается от сложения, так вот почему это делается, потому что я поменял местами эти 2?

Adrian 19.07.2024 18:42

Речь идет не о базовом случае, а о вызове между * и +? так * вызывает мой * не тот, который указан в коде сборки, и то же самое происходит с +? Значит, эти двое звонят друг другу, и никто из них не назовет код, который пришел со Swift?

Adrian 19.07.2024 18:54

Я только что увидел, что вы это сказали, и думаю, вы отредактировали свой комментарий, потому что не думаю, что раньше так было.

Adrian 19.07.2024 18:58

@JoakimDanielson Есть ли способ изменить фактические + и * и не использовать другой инфиксный оператор, потому что здесь вы делаете что-то с функциями, а не со значениями. Я не говорю, что нужно иметь локальную копию +, а затем использовать какую-то логику, я говорю непосредственно о +.

Adrian 19.07.2024 19:06

Y'in + вы вызываете *, а в * вы вызываете +: это будет повторяться вечно, поскольку одна функция вызывает другую.

Ptit Xav 19.07.2024 20:43
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
16
91
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Пожалуйста, опубликуйте ответ, если у вас есть лучший вариант (хочу изменить встроенный + и встроенный *).

infix operator ++
infix operator **

extension Int {
    static funC++ (left: Int, right: Int) -> Int {
        return (left * right)
    }
    
    static func ** (left: Int, right: Int) -> Int {
        return (left + right)
    }
}



// 12
print(3 ++ 4)
// 7
print(3 ** 4)

Расширение внутри функции не допускается (объявление действительно только в области файла), поэтому Swift не позволяет переназначать или затенять операторов в локальных областях, поэтому, если кто-то1 предложил решение, мы очень ценим.

Два оператора, объявленные вами в расширениях, продолжают звонить друг другу. Самый простой способ избежать этого — не использовать + или * в телах этих операторов.

Нам нужен способ вызова встроенных + и * даже при наличии расширения. К счастью, + и * в Int являются протокольными свидетелями требований + и * в Числовом протоколе. Мы можем вызвать + и * через Numeric:

// * and + in these functions will not resolve to the ones you declared in the extension
func add<T: Numeric>(_ lhs: T, _ rhs: T) -> T {
    lhs + rhs
}
func multiply<T: Numeric>(_ lhs: T, _ rhs: T) -> T {
    lhs * rhs
}

// your custom + and * operators can be implemented with the above functions
extension Int {
    static func + (left: Int, right: Int) -> Int {
        return multiply(left, right)
    }
    
    static func * (left: Int, right: Int) -> Int {
        return add(left, right)
    }
}

Конечно, тот же подход не сработает, если вы хотите поменять местами + и * для каждого типа Numeric, но этот трюк работает для любого типа, который «более специфичен», чем Numeric, например FloatingPoint, SignedNumeric и т. д.

Ответ принят как подходящий

Итак, вот то, что я искал:

import Foundation

extension Int {    
    static func + (left: Int, right: Int) -> Int {
        return (left &* right)
    }
        
    static func * (left: Int, right: Int) -> Int {
        return (left &+ right)
    }
}


// 12
print(3 + 4)
// 7
print(3 * 4)

«В Swift операторы &+, &- и &* предоставляют арифметические операции, которые не перехватывают переполнение, а вместо этого оборачиваются с использованием представления дополнения до двух. Это имитирует, сколько языков более низкого уровня (например, C) обрабатывают арифметическое переполнение и как работают аппаратные инструкции для арифметических операций».


Я понятия не имею, что означает приведенное выше объяснение, если кто-нибудь сможет объяснить это подробно, было бы здорово. Я знаю, что &* есть умножение с защитой от переполнения, но что оно делает и как оно используется Swift, я не знаю. Спасибо (просто прокомментируйте под этим ответом).

РЕДАКТИРОВАТЬ

Посмотрите это видео Быстрое преобразование чисел с плавающей запятой Cpp — Кассио Нери — C++, сейчас 2024 год там хорошие вещи. Может быть, каким-то образом это и работает. Если бы кто-нибудь мог рассказать об этом подробнее, это было бы потрясающе.

Некоторая дополнительная информация: Операторы переполнения , и это тоже Таблица 4-битной двоичной системы с методом дополнения ДВА

- &*: оператор &* выполняет умножение и допускает переполнение. В Swift, когда целочисленные операции переполняются, результат оборачивается с использованием арифметики дополнения до двух. Это означает, что если результат превышает максимально представимое значение, он переходит к минимально представимому значению, и наоборот.

Adrian 21.07.2024 17:56

- &+: Аналогично оператор &+ выполняет сложение и допускает переполнение. Он также использует арифметику с дополнением до двух, если результат превышает представимый диапазон типа.

Adrian 21.07.2024 17:57

Более подробная информация приветствуется!

Adrian 21.07.2024 17:57

Другие вопросы по теме