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, в порядке:

  1. Функция сделать один шаг вашего расчета
  2. Начальное значение для вашего расчета
  3. Список (при использовании 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# сделает все остальное.

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