F#: Как вызвать функцию с аргументом Byref Int
У меня есть этот код:
let sumfunc(n: int byref) =
let mutable s = 0
while n >= 1 do
s <- n + (n-1)
n <- n-1
printfn "%i" s
sumfunc 6
Я получаю ошибку:
(8,10): error FS0001: This expression was expected to have type
'byref<int>'
but here has type
'int'
Из этого я могу сказать, в чем проблема, но я просто не знаю, как ее решить. Я думаю, мне нужно указать число 6, чтобы быть byref<int>
как-то. Я просто не знаю как. Моя главная цель здесь состоит в том, чтобы сделать n
или аргумент функции изменяемый, чтобы я мог изменить и использовать его значение внутри функции.
2 ответа
Это такая плохая идея:
let mutable n = 7
let sumfunc2 (n: int byref) =
let mutable s = 0
while n >= 1 do
s <- n + (n - 1)
n <- n-1
printfn "%i" s
sumfunc2 (&n)
Полностью согласен с комментариями Манна, вот еще один способ взорваться:
let sumfunc3 (n: int) =
let mutable s = n
while s >= 1 do
let n = s + (s - 1)
s <- (s-1)
printfn "%i" n
sumfunc3 7
Хорошо для того, чтобы вы были откровенны в том, что это школьное задание, и для того, чтобы выполнять работу самостоятельно, а не просто задавать вопрос, который сводится к "Пожалуйста, сделайте мою домашнюю работу для меня". Поскольку вы были честны об этом, я дам вам более подробный ответ, чем в противном случае.
Во-первых, это очень странное задание. Используя while
цикл и только одна локальная переменная ведет вас по пути повторного использования n
параметр, который является очень плохой идеей. Как правило, функция никогда не должна изменять значения вне ее - и это то, что вы пытаетесь сделать, используя byref
параметр. Как только вы достаточно опытны, чтобы понять, почему byref
в большинстве случаев это плохая идея, вы достаточно опытны, чтобы понять, почему это может - МОЖЕТ - быть необходимым иногда. Но позвольте мне показать вам, почему это плохая идея, используя код, который написал s952163:
let sumfunc2 (n: int byref) =
let mutable s = 0
while n >= 1 do
s <- n + (n - 1)
n <- n-1
printfn "%i" s
let t = ref 6
printfn "The value of t is %d" t.contents
sumfunc t
printfn "The value of t is %d" t.contents
Это выводит:
The value of t is 7
13
11
9
7
5
3
1
The value of t is 0
Вы ожидали этого? Вы ожидали ценность t
изменить только потому, что вы передали его функции? Ты не должен. Вы действительно, ДЕЙСТВИТЕЛЬНО не должны. Функции должны, насколько это возможно, быть "чистыми" - "чистая" функция в терминологии программирования - это та, которая ничего не меняет вне себя - и поэтому, если вы запускаете ее дважды с одним и тем же вводом, она должен выдавать один и тот же результат каждый раз.
Я дам вам способ решить это в ближайшее время, но я собираюсь опубликовать то, что я написал прямо сейчас, чтобы вы это увидели.
ОБНОВЛЕНИЕ: Теперь, вот лучший способ решить это. Во-первых, учитель уже учил рекурсию? Если нет, то вот краткое резюме: функции могут вызывать самих себя, и это очень полезный метод для решения всевозможных проблем. Если вы пишете рекурсивную функцию, вам нужно добавить rec
Ключевое слово сразу после let
, вот так:
let rec sumExampleFromStackru n =
if n <= 0 then
0
else
n + sumExampleFromStackru (n-1)
let t = 7
printfn "The value of t is %d" t
printfn "The sum of 1 through t is %d" (sumExampleFromStackru t)
printfn "The value of t is %d" t
Обратите внимание, что мне не нужно было делать t
изменчивый на этот раз. На самом деле, я мог бы просто позвонить sumExampleFromStackru 7
и это бы сработало.
Теперь это не использует while
цикл, так что это может быть не то, что ищет ваш учитель. И я вижу, что s952163 только что обновил свой ответ другим решением. Но вы должны действительно привыкнуть к идее рекурсии, как только сможете, потому что разбиение проблемы на отдельные этапы с использованием рекурсии является действительно мощной техникой для решения многих проблем в F#. Так что, хотя это не тот ответ, который вы ищете прямо сейчас, это ответ, который вы будете искать в ближайшее время.
PS Если вы используете какую-либо помощь, полученную здесь, скажите своему учителю, что вы сделали это, и дайте ему URL этого вопроса (http://stackru.com/questions/39698430/f-how-to-call-a-function-with-argument-byref-int
) чтобы он мог прочитать то, что вы спросили, и что другие люди сказали вам. Если он хороший учитель, он не понизит вашу оценку за это; на самом деле, он может поднять это за честность и искренность в том, как вы решили проблему. Но если вы получили помощь в выполнении домашней работы и не сказали своему учителю, 1) это нечестно, и 2) вы только навредите себе в долгосрочной перспективе, потому что он подумает, что вы понимаете концепцию, которую вы, возможно, не поняли. Я еще не понял.
ОБНОВЛЕНИЕ 2: s952163 предлагает, чтобы я показал вам, как использовать fold
а также scan
функции, и я подумал "почему бы и нет?" Имейте в виду, что это передовые методы, поэтому вы, вероятно, не будете получать задания там, где вам нужно использовать fold
какое-то время. Но fold
это в основном способ взять любой список и сделать вычисление, которое превращает список в одно значение, в общем виде. С fold
вы указываете три вещи: список, с которым вы хотите работать, начальное значение для вашего вычисления и функцию двух параметров, которые будут выполнять один шаг вычисления. Например, если вы пытаетесь сложить все числа от 1 до n
Ваша функция "один шаг" будет let add a b = a + b
, (Есть еще более продвинутая функция F#, которую я пропускаю в этом объяснении, потому что вы должны изучать только одну вещь за раз. Пропуская ее, она сохраняет add
функция проста и понятна.)
Как вы будете использовать fold
выглядит так:
let sumWithFold n =
let upToN = [1..n] // This is the list [1; 2; 3; ...; n]
let add a b = a + b
List.fold add 0 upToN
Обратите внимание, что я написал List.fold
, Если upToN
был массив, то я бы написал Array.fold
вместо. Аргументы fold
, будь то List.fold
или же Array.fold
, в порядке:
- Функция сделать один шаг вашего расчета
- Начальное значение для вашего расчета
- Список (при использовании
List.fold
) или массив (если используетсяArray.fold
) что вы хотите сделать расчет с.
Позвольте мне пройти через что List.fold
делает. Мы представим, что вы вызвали свою функцию с 4 в качестве значения n
,
Первый шаг: список [1;2;3;4]
и внутренний valueSoFar
переменная внутри List.fold
устанавливается в начальное значение, которое в нашем случае равно 0.
Далее: функция расчета (в нашем случае add
) называется с valueSoFar
в качестве первого параметра и первый элемент списка в качестве второго параметра. Итак, мы называем add 0 1
и получим результат 1. Внутренний valueSoFar
переменная обновляется до 1, а остальная часть списка [2;3;4]
, Так как это еще не пусто, List.fold
продолжит бежать.
Далее: функция расчета (add
) называется с valueSoFar
в качестве первого параметра и первый элемент остальной части списка в качестве второго параметра. Итак, мы называем add 1 2
и получим результат 3. Внутренний valueSoFar
переменная обновляется до 3, а остальная часть списка [3;4]
, Так как это еще не пусто, List.fold
продолжит бежать.
Далее: функция расчета (add
) называется с valueSoFar
в качестве первого параметра и первый элемент остальной части списка в качестве второго параметра. Итак, мы называем add 3 3
и получите результат 6. Внутренний valueSoFar
переменная обновлена до 6, а остальная часть списка [4]
(это список с одним элементом, номер 4). Так как это еще не пусто, List.fold
продолжит бежать.
Далее: функция расчета (add
) называется с valueSoFar
в качестве первого параметра и первый элемент остальной части списка в качестве второго параметра. Итак, мы называем add 6 4
и получим результат 10. Внутренний valueSoFar
переменная обновляется до 10, а остальная часть списка []
(это пустой список). Поскольку остальная часть списка теперь пуста, List.fold
остановится и вернет текущее значение valueSoFar
как его окончательный результат.
Так зовет List.fold add 0 [1;2;3;4]
по существу вернется 0+1+2+3+4
или 10.
Теперь поговорим о scan
, scan
функция так же, как fold
Функция, за исключением того, что вместо того, чтобы возвращать только конечное значение, она возвращает список значений, созданных на всех этапах (включая начальное значение). (Или если вы позвонили Array.scan
, он возвращает массив значений, созданных на всех этапах). Другими словами, если вы звоните List.scan add 0 [1;2;3;4]
, он проходит те же шаги, что и List.fold add 0 [1;2;3;4]
, но он создает список результатов, как он делает каждый шаг вычисления, и возвращает [0;1;3;6;10]
, (Начальное значение - это первый элемент списка, затем каждый шаг расчета).
Как я уже сказал, это продвинутые функции, которые ваш учитель пока не будет освещать. Но я решила, что разожгу ваш аппетит к тому, что F# может сделать. Используя List.fold
Вам не нужно писать while
петля или for
цикл, или даже использовать рекурсию: все, что сделано для вас! Все, что вам нужно сделать, это написать функцию, которая выполняет один шаг вычисления, а F# сделает все остальное.