Add Function That Uses Single Variable to Merge Results to Itself

You appear to have done a lot of emphasis on String.Concat. Will a function like an Array.min work with any of these? I’m noticing results similar to the problem that was originally identified in one of your posts regarding return single values.

You appear to have done a lot of emphasis on String.Concat.

So in the F# type system, a value must have a single type, like string or string[] or int[], which are all distinct. You can of course use a function to convert between those types. My confusion is you seem to want two different types from the output: string (like "2;3;4;5;") and also int[] (like [|2;3;4;5|] which you could call Array.min on). Any given value can only have one of those types, and you can’t call Array.min "2;3;4;5;", because Array.min takes an array, and "2;3;4;5;" is a string. But you can convert between int[] and string using functions like String.Join, String.Concat, and String.Split. For example, a couple functions here can turn an int[] into a string:

let convert1 (xs:int[]) : string = String.Join(";", xs |> Array.map string)
let convert2 (xs:int[]) : string = [ for x in xs -> sprintf "%i;" ] |> String.Concat
convert1 [|2;3;4;5|] // returns "2;3;4;5"
convert2 [| for i in "2 3 4 5".Split(' ') -> int i |] // returns "2;3;4;5;"

I’m using functions like these a lot because my understanding is you want both an int[] to call Array.min on and a string representation with semicolons separating each int. Maybe you’re trying to make a value that acts like both things? That would not be possible.

Can you explain why a StringBuilder() will only work to achieve our goal like this:

open System
open System.Text

let str = "1 2 3 4 5 6"
let Spl = str.Split(' ')

let mutable builder = StringBuilder()

for i in 1..Spl.Length - 2  do
  builder.Append(sprintf "%s;" Spl.[i]) |> ignore

let result = string builder
printf "%A" result

And not like this:

open System
open System.Text

let str = "1 2 3 4 5 6"
let Spl = str.Split(' ')
for i in 1..Spl.Length - 2  do    

let v = Spl.[i]

let mutable builder = StringBuilder()

builder.Append(sprintf "%s;" v) |> ignore

let result = string builder
printf "%s" result

In your second code snippet, unfortunately none of the indents came through (F# is a whitespace-sensitive language, so the meaning changes depending on which lines were indented). If you meant

open System
open System.Text

let str = "1 2 3 4 5 6"
let Spl = str.Split(' ')
for i in 1..Spl.Length - 2  do    

  let v = Spl.[i]

  let mutable builder = StringBuilder()

  builder.Append(sprintf "%s;" v) |> ignore

let result = string builder
printf "%s" result

then it doesn’t compile because a for loop creates its own scope. When you define let mutable builder = StringBuilder() inside the loop, it only exists inside the loop. You have to define it outside the loop for it to be accessible in the final 2 lines.
If you meant

open System
open System.Text

let str = "1 2 3 4 5 6"
let Spl = str.Split(' ')
for i in 1..Spl.Length - 2  do    

  let v = Spl.[i]

  let mutable builder = StringBuilder()

  builder.Append(sprintf "%s;" v) |> ignore

  let result = string builder
  printf "%s" result

then it compiles, but the builder is sort of unnecessary. You’re not actually “pushing” values to the string in each iteration, in each iteration you make a new StringBuilder which only has one value in it. It could just be simplified to

open System
open System.Text

let str = "1 2 3 4 5 6"
let Spl = str.Split(' ')
for i in 1..Spl.Length - 2  do    

  let v = Spl.[i]

  let result = sprintf "%s;" v
  printf "%s" result

and you can verify you get the same result. There’s nothing wrong with that, it’s just that you’re “pushing” values directly to the console, and once you leave the loop you don’t have access to any those values. And inside the loop, you only have access to each individual element (not all of the processed elements so far). But if all you need to do is print those values to the console, that works just fine. Though it would be more idiomatic and succinct to replace the whole for loop with just the single line

printf "%s;" (String.Join(";", Spl.[1 .. Spl.Length - 2]))

Do you know of any accumulator helper functions that are more certain to work here? Resolve several single values pushed into a long system of code in the middle of a script not containing after they’ve been parsed and map, rejoin, or contain all the values for Continuation?

Yes, all 4 of the code examples from my post on 2022-01-07 feature single values being pushed into a collection from the middle of a for loop, where that collection can then be used from outside the loop. (That would be the 2 forms of list comprehensions, the ResizeArray and the StringBuilder)

Like this?

let str = "1 2 3 4 5 6"
let Spl = str.Split(' ')
for i in 1..Spl.Length - 2  do    

  let v = Spl.[i]

//DU goes here//

//results pipe out here//

//join, map, or collection happens here//

//do something function using int or any arithmetic for Continuation//

How’s this?

open System
type NumbersYouCareAbout = EvenNumbers | OddNumbers
let whatNumbersDoYouCareAbout = EvenNumbers
let str = "1 2 3 4 5 6"
let Spl = str.Split(' ')
let processedNumbers : float list = 
  [ 
    for i in 1..Spl.Length - 2  do
      let v : float = float Spl.[i]
      match whatNumbersDoYouCareAbout with
      | OddNumbers -> 
        if v % 2.0 <> 0.0 then yield v
      | EvenNumbers -> 
        if v % 2.0 = 0.0 then yield v
  ]

let result = List.average processedNumbers
printfn "the average of [ %s ] is %f" (String.Join(";", processedNumbers |> List.map string)) result
1 Like

I might be confusing you. So, this is my DU now:

open System
open System.Collections.Generic

let Data = "1 2 3" 
let Res = Data.ToString()
let Spl = (Res:string).Split(' ')

for i in 0..Spl.Length - 1  do    
let v = Spl.[i]
let k = i

let Find L =
    let dic = new Dictionary<string,string>()
    dic.Add(k.ToString(),v)
    
    let mutable Div=""
   
    for obj in L do
        let k,v = dic.TryGetValue(obj)      
        Div <-if k then v else Div            
    Div
   
Find ["0";"1";"2"] |> fun eq ->
    eq
     
    if (eq.Length >= 1) then

    printf "%A" eq

Rejoin, map, or apply collection so that continuation and arithmetic eg. Array.min is possible after using DU key to list designated table related values.

Okay, what about this

open System
open System.Collections.Generic

let Data = "1 2 3" 
let Res = Data.ToString()
let Spl = (Res:string).Split(' ')

let allEqValues = 
  [
    for i in 0..Spl.Length - 1 do    
      let v = Spl.[i]
      let k = i

      let Find L =
          let dic = new Dictionary<string,string>()
          dic.Add(k.ToString(),v)
          
          let mutable Div=""
        
          for obj in L do
              let k,v = dic.TryGetValue(obj)      
              Div <-if k then v else Div            
          Div
      
      let eq = Find ["0";"1";"2"] 
      if (eq.Length >= 1) then 
        printf "%A" eq
        yield eq
  ]

let min = List.min (List.map int allEqValues)

As an aside, this is a fairly imperative (and thus verbose) implementation of the find function. A more idiomatic F# solution might look more like

let find L = if L |> Seq.contains k then v else ""

As a principled substitute for IDictionary? Thanks heck! You’re the greatest, ntwilson!!!

1 Like