2016-01-07 2 views
3

Я хотел бы объединить следующие строки:Как объединить два вызова функций в один?

let result1 = add (numbers, ",") 
let result2 = add (numbers, "\n") 

во что-то вроде этого:

let resultX = add (numbers, ",") |> add (numbers, "\n") 

Могу ли я сочинить такие функции, как это?

Примечание:

Я учусь F # и прошу прощения, если этот вопрос кажется глупым.

код ниже:

module Calculator 

open FsUnit 
open NUnit.Framework 
open System 

let add (numbers:string) = 

    let add (numbers:string) (delimiter:string) = 
     if (numbers.Contains(delimiter)) then 
      numbers.Split(delimiter.Chars(0)) |> Array.map Int32.Parse 
               |> Array.sum 
     else 0 

    let result1 = add numbers "," 
    let result2 = add numbers "\n" 

    if (result1 > 0 || result2 > 0) then 
     result1 + result2 

    else let _ , result = numbers |> Int32.TryParse 
     result 

Тесты:

[<Test>] 
let ``adding empty string returns zero``() = 

    let result = add "" 
    result |> should equal 0 

[<Test>] 
let ``adding one number returns number``() = 

    let result = add "3" 
    result |> should equal 3 

[<Test>] 
let ``add two numbers``() = 

    let result = add "3,4" 
    result |> should equal 7 

[<Test>] 
let ``add three numbers``() = 

    let result = add "3,4,5" 
    result |> should equal 12 

[<Test>] 
let ``line feeds embedded``() = 

    let result = add "3\n4" 
    result |> should equal 7 

ОБНОВЛЕНО

Я получаю следующее сообщение об ошибке:

The type 'int' does not match the type 'string'

let add (numbers:string) = 

    let add (numbers:string) (delimiter:string) = 
     if (numbers.Contains(delimiter)) then 
      numbers.Split(delimiter.Chars(0)) |> Array.map Int32.Parse 
               |> Array.sum 
     else 0 

let resultX = numbers |> add "," 
         |> add "\n" 

Реализовано Обратная связь:

let add (numbers:string) = 

    let add (numbers:string) (delimiters:char array) = 
     if numbers.Length = 0 then 0 
     else numbers.Split(delimiters) |> Array.map Int32.Parse 
             |> Array.sum 
    let delimiters = [|',';'\n'|] 
    add numbers delimiters 
+1

Требуется ли, чтобы каждый разделитель выполнялся отдельным вызовом функции добавления? Вы пытаетесь узнать о составе функции или просто просто суммировать строку? Если вы пытаетесь суммировать строку, я предлагаю вам сделать один вызов, передающий все разделители как массив в [Split] (https://msdn.microsoft.com/en-us/library/b873y76a (v = vs .110) .aspx). –

+0

Мы должны задать вопрос: «Тип« int »не соответствует типу« строка »отдельного вопроса. Если бы вы могли спросить об этом, я мог бы переместить эту часть ответа на него. Это действительно два вопроса, и другая часть должна быть легче найти. –

ответ

3

Это не точный ответ, как я не уверен, что вы имеете в виду, но это должно дать вам некоторые идеи.

let add01 (numbers:string) = 
    let delimiters : char array = [|',';'\n'|] 
    let inputArray : string array = numbers.Split(delimiters) 
    let numbers : string list = Array.toList(inputArray) 
    let rec add (numbers : string list) (total : int) : int = 
     match (numbers : string list) with 
     | ""::t -> 
      add t total 
     | h::t -> 
      let number = System.Int32.Parse h 
      let total = total + number 
      add t total 
     | [] -> total   
    add numbers 0 

let numbers = "1,2,3\n4,5,6\n\n" 
let result = add01 numbers 

Приведенный ниже код вызывает ошибку, почему?

// Type mismatch. Expecting a 
//  int -> 'a  
// but given a 
//  string -> int  
// The type 'int' does not match the type 'string' 
let result = numbers |> add "," 
        |> add "\n" 

Поскольку это ошибка о том, что два типа не согласны нужно понимать type inferencing и как решать такие проблемы. Я не буду объяснять здесь вывод типа, поскольку это большая тема сама по себе, однако я приведу пример шаблона, который успешно работает с большинством времени для меня при разрешении таких ошибок.

Когда F # компилирует код, он использует вывод типа, чтобы добавить недостающие типы к функциям и значениям перед выполнением проверки типа, и это проверка типа, которая терпит неудачу. Таким образом, чтобы увидеть, что видит компилятор для типов, мы вручную добавим их здесь и отделим части кода, которые не вызывают проблемы, оставляя нас с причиной ошибки, надеюсь, что-то в этом случае становится очевидным для исправления.

Единственные вещи, которые имеют следующие типы:

  • результат
  • =
  • номера
  • |>
  • добавить
  • ""
  • "\ п"

типов для значений легко:

  • результата: INT
  • номера: строка
  • "": строка
  • "\ п": строка

Я надеваю Напомню, что F # обрабатывает equals (=) как функцию, но вот как это можно понять.
=: 'а ->' оператор А

трубопровода

let (|>) (x : 'a) f = f (x : 'a) 

Для решения проблемы просто думаю оператора трубопровода, как syntactic sugar.
См. Примеры ниже для лучшего понимания.

Надстройка функция
добавить: строка -> строка -> Int

Так позволяет уточнить ошибку вплоть до своей сути.

//Type mismatch. Expecting a 
// int -> 'a  
//but given a 
// string -> int  
//The type 'int' does not match the type 'string' 
let result = numbers |> add "," 
        |> add "\n" 

Добавьте сигнатуры типа к значениям и убедитесь, что мы получим ту же ошибку. Это то, что сделал бы метод вывода, и мы сделали это вручную.

//Type mismatch. Expecting a 
// int -> int  
//but given a 
// string -> int  
//The type 'int' does not match the type 'string' 
let (result : int) = (numbers : string) |> add ("," : string) 
        |> add ("\n" : string) 

Теперь подумайте о коде как математическом выражении, которое может быть учтено.

Фактор первого оператора трубопровода и убедитесь, что мы получаем ту же ошибку. Уведомление об ошибке в настоящее время только часть r2

//Expecting a 
// int -> 'a  
//but given a 
// string -> int  
//The type 'int' does not match the type 'string' 
let (result : int) = 
    let r1 = (numbers : string) |> add ("," : string) 
    let r2 = r1 |> add ("\n" : string) 
    r2 

Отменить синтаксический сахар для второго оператора трубопровода и проверки мы получаем ту же ошибку. Обратите внимание, что ошибка теперь является только частью r2; в частности аргумент r1

//This expression was expected to have type 
// string  
//but here has type 
// int 
let (result : int) = 
    let r1 = (numbers : string) |> add ("," : string) 
    let r2 = add ("\n" : string) r1 
    r2 

Добавить тип в r1 и убедиться, что мы получим ту же ошибку.

//This expression was expected to have type 
// string  
//but here has type 
// int 
let (result : int) = 
    let (r1 : int) = (numbers : string) |> add ("," : string) 
    let r2 = add ("\n" : string) r1 
    r2 

На этом этапе ошибка должна быть очевидной. Результатом первого оператора конвейера является int и передается функции добавления в качестве второго аргумента. Функция добавления ожидает string для второго аргумента, но ему присваивается int.


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

Это вспомогательные функции для демонстрации.

let output1 w = 
    printfn "1: %A" w 

let output2 w x = 
    printfn "1: %A 2: %A" w x 

let output3 w x y = 
    printfn "1: %A 2: %A 3: %A" w x y 

let output4 w x y z = 
    printfn "1: %A 2: %A 3: %A 4: %A" w x y z 

Использование выходных функций без оператора трубопровода.

output1 "a" 
1: "a" 

output2 "a" "b" 
1: "a" 2: "b" 

output3 "a" "b" "c" 
1: "a" 2: "b" 3: "c" 

output4 "a" "b" "c" "d" 
1: "a" 2: "b" 3: "c" 4: "d" 

Обратите внимание, что выход находится в том же порядке, что и вход.

Использование выходных функций с оператором трубопровода.

// пусть (|>) = XF FX

"a" |> output1 
1: "a" 

"a" |> output2 "b" 
1: "b" 2: "a" 

"a" |> output3 "b" "c" 
1: "b" 2: "c" 3: "a" 

"a" |> output4 "b" "c" "d" 
1: "b" 2: "c" 3: "d" 4: "a" 

ВНИМАНИЕ, что последний аргумент для выходных функций значение слева от оператора трубопровода («а») из-за использования оператора трубопровода (|>).

// См. Раздел 3.7 раздела F# specification о том, как определить определяемые пользователем операторы.

Использование выходных функций с помощью определенного оператора конвейера.

let (@.) x f = f x 

"a" @. output1 
1: "a" 

"a" @. output2 "b" 
1: "b" 2: "a" 

"a" @. output3 "b" "c" 
1: "b" 2: "c" 3: "a" 

"a" @. output4 "b" "c" "d" 
1: "b" 2: "c" 3: "d" 4: "a" 
+0

Вы ответили на мой вопрос. Это значение разделителя. –

+0

Я не был уверен, что ваш фокус для обучения был, но рад, что вы что-то узнали сегодня. Потратьте время, чтобы прочитать расширенный ответ, это будет то, что вы вернетесь на регулярной основе. –

3

Я не знаю ни одного универсального способа сочинить такие функции, как вы, кажется, спрашивать, но если вам нужно только изменять один аргумент, один вариант заключается в создании списка аргументов, а затем карту над те:

let results = [","; "\n"] |> List.map (add numbers) 

Если вы сделаете это, то results является int list, а затем вы должны решить, что делать с этим списком. В этом случае представляется целесообразным суммировать по списку, но с учетом текущих условий, которые проверяют, являются ли result1 или result2 положительными, это не кажется подходящим.

Все, что было сказано, учитывая, что в настоящее время поставляются тестеры, нет причин сделать его более сложным, чем это должно быть. Эта реализация также проходит все испытания:

let add = 
    let split (x : string) = 
     x.Split([| ','; '\n' |], StringSplitOptions.RemoveEmptyEntries) 
    split >> Array.map Int32.Parse >> Array.sum 

Это не особенно надежная реализация, как это будет ошибки, если строка содержит символы, которые не могут быть разобраны в целые числа, но так будет реализация ОП.

+0

Спасибо, Марк. Вы только что определили ошибку в моей логике относительно более нуля. –

Смежные вопросы