[swift-evolution] Strings in Swift 4

Ted F.A. van Gaalen tedvgiosdev at gmail.com
Sat Feb 25 09:46:49 CST 2017


Hi David W. 
please read inline responses
> On 25 Feb 2017, at 07:26, David Waite <david at alkaline-solutions.com> wrote:
> 
> Ted, 
> 
> It might have helped if instead of being called String and Character, they were named Text and ExtendedGraphemeCluster. 
Imho,l “text” maybe, but in computer programming “String” is more appropriate, I think. see:
https://en.wikipedia.org/wiki/String_(computer_science) <https://en.wikipedia.org/wiki/String_(computer_science)> 

Also imho, “Character” is OK (but maybe “Symbol” would be better)  because mostly, when working
with text/strings in an application it is not important to know how the Character is encoded, 
e.g. Unicode, ASCII, whatever.(OOP , please hide the details, thank you)  

However, If I needed to work  with the character’s components directly, e.g. when I 
might want to influence the display of
the underlying graphical aspects, I always have access to the Characters’ properties 
and methods. Unicode codepoints, ASCII bytes.. whatever it contains... 
   
> 
> They don’t really have the same behavior or functionality as string/characters in many other languages, especially older languages. This is because in many languages, strings are not just text but also random-accesss (possibly binary) data.
could be but that’s not my perception of a string. 
> 
> Thats not to say that there aren’t a ton of algorithms where you can use Text like a String, treat ExtendedGraphemeCluster like a character, and get unicode behavior without thinking about it.
> 
> But when it comes to random access and/or byte modification, you are better off working with something closer to a traditional (byte) string interface.
> 
> Trying to wedge random access and byte modification into the Swift String will simply complicate everything, slow down the algorithms which don’t need it, eat up more memory, as well as slow down bridging between Swift and Objective C code.
Yes, this has been extensively discussed in this thread...
> 
> Hence me suggesting earlier working with Data, [UInt8], or [Character] within the context of your manipulation code, then converting to a Swift String at the end. Convert to the data format you need, then convert back.
That’s exactly what I did, saved that I have the desire to work exclusively with discrete
 (in the model of humanly visible discrete elements on a graphical medium) ... 

For the sake of completeness ,here is my complete Swift 3.x playground example, may useful for others too:
//: Playground - noun: a place with Character!

import UIKit
import Foundation

struct TGString: CustomStringConvertible
{
    var ar = [Character]()
    
    var description: String // used by "print" and "\(...)"
    {
        return String(ar)
    }
    
    // Construct from a String
    init(_ str : String)
    {
        ar = Array(str.characters)
    }
    // Construct from a Character array
    init(_ tgs : [Character])
    {
        ar = tgs
    }
    // Construct from anything. well sort of..
    init(_ whatever : Any)
    {
        ar = Array("\(whatever)".characters)
    }
    
    var $: String
        {
        get                               // return as a normal Swift String
        {
            return String(ar)
        }
        set (str)                         //Mutable: set from a Swift String
        {
            ar = Array(str.characters)
        }
    }
    
    var asString: String
        {
        get                               // return as a normal Swift String
        {
            return String(ar)
        }
        set (str)                         //Mutable: set from a Swift String
        {
            ar = Array(str.characters)
        }
    }
    
    // Return the count of total number of characters:
    var count: Int
        {
        get
        {
            return ar.count
        }
    }
    
    // Return empty status:
    
    var isEmpty: Bool
        {
        get
        {
            return ar.isEmpty
        }
    }
    
    // s[n1..<n2]
    subscript (n: Int) -> TGString
        {
        get
        {
            return TGString( [ar[n]] )
        }
        set(newValue)
        {
            if newValue.isEmpty
            {
                ar.remove(at: n) // remove element when empty
            }
            else
            {
                ar[n] =  newValue.ar[0]
                if newValue.count > 1
                {
                    insert(at: n, string: newValue[1..<newValue.count])
                }
            }
        }
    }

    subscript (r: Range<Int>) -> TGString
        {
        get
        {
            return TGString( Array(ar[r]) )
        }
        set(newValue)
        {
            ar[r] = ArraySlice(newValue.ar)
        }
    }

    subscript (r: ClosedRange<Int>) -> TGString
    {
        get
        {
            return TGString( Array(ar[r]) )
        }
        set(newValue)
        {
            ar[r] = ArraySlice(newValue.ar)
        }
    }

    func right( _ len: Int) -> TGString
    {
        var l = len
        
        if l > count
        {
            l = count
        }
        return TGString(Array(ar[count - l..<count]))
    }
    
    func left(_ len: Int) -> TGString
    {
        var l = len
        
        if l > count
        {
            l = count
        }
        return TGString(Array(ar[0..<l]))
    }
    
    func mid(_ pos: Int, _ len: Int) -> TGString
    {
        if pos >= count
        {
            return TGString.empty()
        }
        
        var l = len
        
        if l > pos + len
        {
            l = pos + len
        }
        return TGString(Array(ar[pos..<pos + l]))
    }
    
    func mid(_ pos: Int) -> TGString
    {
        if pos >= count
        {
            return TGString.empty()
        }
        
        return TGString(Array(ar[pos..<count]))
    }
    
    mutating func insert(at: Int, string: TGString)
    {
        ar.insert(contentsOf: string.ar, at: at)
    }

    // Concatenate
    static func + (left: TGString, right: TGString) -> TGString
    {
        return(TGString(left.ar + right.ar) )
    }
    
    // Return an empty TGString:
    static func empty() -> TGString
    {
        return TGString([Character]())
    }
} // end TGString

// trivial isn’t? but effective... 
var strabc = "abcdefghjiklmnopqrstuvwxyz"
var strABC = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
var abc = TGString(strabc)
var ABC = TGString(strABC)

func test()
{
    // as in Basic: left$, mid$, right$
    print(abc.left(5))
    print(abc.mid(5,10))
    print(ABC.mid(5))
    print(ABC.right(5))
    // ranges and concatenation:
    print(abc[12..<23])
    print(abc.left(5) + ABC.mid(6,6) + abc[10...25])
    
    // eat anything:
    let d:Double = -3.14159
    print(TGString(d))
 
    let n:Int = 1234
    print(TGString(n))
    
    print(TGString(1234.56789))
    
    let str = abc[15..<17].asString      // Copy to to normal Swift String
    print(str)
    
    let s = "\(abc[12..<20])" // interpolate to normal Swift String.
    print(s)
    
    abc[3..<5] = TGString("34") // if lenghts don't match:
    abc[8...9] = ABC[24...25]   //  length of dest. string is altered.
    abc[12] = TGString("$$$$")  //  if src l > 1 will insert remainder after dest.12 here
    abc[14] = TGString("")      //  empty removes character at pos.
    print(abc)
    abc.insert(at: 3, string: ABC[0..<3])
    print(abc)
}
 
test()
 
outputs this: 
abcde
fghjiklmno
FGHIJKLMNOPQRSTUVWXYZ
VWXYZ
mnopqrstuvw
abcdeGHIJKLklmnopqrstuvwxyz
-3.14159
1234
1234.56789
pq
mnopqrst
abc34fghYZkl$$$nopqrstuvwxyz
abcABC34fghYZkl$$$nopqrstuvwxyz

> 
> Thats not to say that there aren’t features which would simplify/clarify algorithms working in this manner.
> 
true. 
This discussion was interesting, triggers further thinking, 
maybe even more because it touched more principal considerations

As you know, of course, a programming language is always a compromise between human
and computer “The Machine” so to speak. It started years ago with writing
Assembler then came higher PLs like Fortran, PL/1 Cobol etc. later C and C++
to just name a few… Also we see deviations in directions like OOP FP.. 
(and everybody thinks they’re right of course even me :o)

What (even in this time (2017))  often seems to be an unavoidable obstacle
is the tradeoff/compromise speed/distance-from-the machine, that is,
how far optimisation aspects are emerging/surfacing through all these 
layers of abstraction into the upper levels of the programming language... 

In this view, the essence of this discussion was perhaps then not the triviality 
wether or not one should instantiate a character array or not, but rather that 
obviously (not only) in Swift these underlying optimisation aspects more or
less form a undesired restriction… ? 

TedvG.
1980 - from Yes song: "Machine Messiah" - read the lyrics also: very much in context here!  

> -DW
> 
>> On Feb 24, 2017, at 4:27 PM, Ted F.A. van Gaalen via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> ok, I understand, thank you
>> TedvG
>>> On 25 Feb 2017, at 00:25, David Sweeris <davesweeris at mac.com <mailto:davesweeris at mac.com>> wrote:
>>> 
>>> 
>>>> On Feb 24, 2017, at 13:41, Ted F.A. van Gaalen <tedvgiosdev at gmail.com <mailto:tedvgiosdev at gmail.com>> wrote:
>>>> 
>>>> Hi David & Dave
>>>> 
>>>> can you explain that in more detail?
>>>>>> Wouldn’t that turn simple character access into a mutating function?
>>>> 
>>>> assigning like   s[11…14] = str  is of course, yes.
>>>> only then - that is if the character array thus has been changed - 
>>>> it has to update the string in storage, yes. 
>>>> 
>>>> but  str = s[n..<m]   doesn’t. mutate.
>>>> so you’d have to maintain keep (private) a isChanged: Bool or bit.
>>>> a checksum over the character array .  
>>>> ?
>>> 
>>> It mutates because the String has to instantiate the Array<Character> to which you're indexing into, if it doesn't already exist. It may not make any externally visible changes, but it's still a change.
>>> 
>>> - Dave Sweeris
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170225/308a0f51/attachment.html>


More information about the swift-evolution mailing list